<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Aurelio Buarque</title>
    <description>The latest articles on DEV Community by Aurelio Buarque (@buarki).</description>
    <link>https://dev.to/buarki</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1197876%2Fff6ddec6-5788-4eab-b246-b0a87593580a.png</url>
      <title>DEV Community: Aurelio Buarque</title>
      <link>https://dev.to/buarki</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/buarki"/>
    <language>en</language>
    <item>
      <title>Optimizing Struct Layout and Padding in Practice</title>
      <dc:creator>Aurelio Buarque</dc:creator>
      <pubDate>Tue, 27 May 2025 01:52:07 +0000</pubDate>
      <link>https://dev.to/buarki/optimizing-struct-layout-and-padding-in-practice-23p1</link>
      <guid>https://dev.to/buarki/optimizing-struct-layout-and-padding-in-practice-23p1</guid>
      <description>&lt;h2&gt;
  
  
  What is Struct Layout?
&lt;/h2&gt;

&lt;p&gt;When working with Go, understanding how structs are laid out in memory is crucial for writing efficient code. Struct layout refers to how fields within a struct are arranged in memory, including any padding that the compiler adds to ensure proper alignment.&lt;/p&gt;

&lt;p&gt;In Go, the compiler follows specific rules for struct layout to ensure efficient memory access and proper alignment with the underlying hardware architecture. This process is automatic, but understanding it can help you write more memory-efficient code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why is Struct Layout Important?
&lt;/h2&gt;

&lt;p&gt;The way structs are laid out in memory can have significant implications for your application's performance and memory usage. Here's why it matters:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Memory Efficiency&lt;/strong&gt;: Poor struct layout can lead to wasted memory due to padding. The compiler adds padding bytes to ensure that fields are properly aligned, which can sometimes result in significant memory overhead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Cache Utilization&lt;/strong&gt;: Modern CPUs use cache lines (typically 64 bytes) to fetch data from memory. If your struct fields are scattered due to padding, you might need more cache lines to access the same amount of data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Performance Impact&lt;/strong&gt;: Accessing memory that isn't properly aligned can lead to performance penalties, as the CPU might need to perform multiple memory accesses to read a single value.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding Padding
&lt;/h2&gt;

&lt;p&gt;Take a look at this one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Example&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;    &lt;span class="c"&gt;// 1 byte&lt;/span&gt;
    &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="kt"&gt;int64&lt;/span&gt;   &lt;span class="c"&gt;// 8 bytes&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;    &lt;span class="c"&gt;// 1 byte&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You might expect this struct to take 10 bytes (1 + 8 + 1), but due to alignment requirements, it actually takes 24 bytes! Here's why:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;The int64 field needs to be aligned on an 8-byte boundary;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The compiler adds 7 bytes of padding after the first bool;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It also adds 7 bytes of padding after the second bool to maintain alignment for potential subsequent fields;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We can optimize this by reordering the fields:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;OptimizedExample&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="kt"&gt;int64&lt;/span&gt;   &lt;span class="c"&gt;// 8 bytes&lt;/span&gt;
    &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;    &lt;span class="c"&gt;// 1 byte&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;    &lt;span class="c"&gt;// 1 byte&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This optimized version only takes 16 bytes because the bool fields can share the same padding space. The rule of thumb is: &lt;strong&gt;place larger fields first&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practical Benefits
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Reduced Memory Usage&lt;/strong&gt;: In systems where memory is constrained or when dealing with large numbers of structs, proper layout can significantly reduce memory consumption.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Better Cache Performance&lt;/strong&gt;: When structs are properly laid out, more data can fit into a single cache line, reducing cache misses and improving performance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Improved Serialization&lt;/strong&gt;: When sending structs over the network or storing them on disk, proper layout can reduce the amount of data that needs to be transferred or stored.&lt;/p&gt;

&lt;h2&gt;
  
  
  Best Practices
&lt;/h2&gt;

&lt;p&gt;Here are some best practices for struct layout in Go:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Order fields by size&lt;/strong&gt;: Place larger fields first, followed by smaller ones.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Group related fields&lt;/strong&gt;: Keep related fields together to improve cache locality.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Consider alignment requirements&lt;/strong&gt;: Be aware of the alignment requirements of different types (e.g., int64 needs 8-byte alignment).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Use tools&lt;/strong&gt;: Leverage tools like viztruct to analyze and optimize your struct layouts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practical Benchmark
&lt;/h2&gt;

&lt;p&gt;I've created a &lt;a href="https://gist.github.com/buarki/27824ef45d47e2a39795a9d8cfff9bb9" rel="noopener noreferrer"&gt;simple benchmark&lt;/a&gt; to demonstrate the impact of struct layout on performance and memory usage. It compares the two following structs: one with poor layout and one with optimized layout:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Poor layout - fields ordered by size&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;PoorLayout&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;    &lt;span class="c"&gt;// 1 byte&lt;/span&gt;
    &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="kt"&gt;int64&lt;/span&gt;   &lt;span class="c"&gt;// 8 bytes&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;    &lt;span class="c"&gt;// 1 byte&lt;/span&gt;
    &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="kt"&gt;int32&lt;/span&gt;   &lt;span class="c"&gt;// 4 bytes&lt;/span&gt;
    &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;    &lt;span class="c"&gt;// 1 byte&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// Optimized layout - larger fields first&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;OptimizedLayout&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="kt"&gt;int64&lt;/span&gt;   &lt;span class="c"&gt;// 8 bytes&lt;/span&gt;
    &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="kt"&gt;int32&lt;/span&gt;   &lt;span class="c"&gt;// 4 bytes&lt;/span&gt;
    &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;    &lt;span class="c"&gt;// 1 byte&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;    &lt;span class="c"&gt;// 1 byte&lt;/span&gt;
    &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;    &lt;span class="c"&gt;// 1 byte&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The outputs I got from this benchmarks are:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;go &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;-bench&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-benchmem&lt;/span&gt;
goos: darwin
goarch: amd64
pkg: github.com/buarki/chvv
cpu: VirtualApple @ 2.50GHz
BenchmarkMemoryAllocation/PoorLayout-12                 1207        843062 ns/op    32006154 B/op          1 allocs/op
BenchmarkMemoryAllocation/OptimizedLayout-12            2422        447033 ns/op    16007174 B/op          1 allocs/op
BenchmarkFieldAccess/PoorLayout-12                      2146        567032 ns/op           0 B/op          0 allocs/op
BenchmarkFieldAccess/OptimizedLayout-12                 4645        257307 ns/op           0 B/op          0 allocs/op
PASS
ok      github.com/buarki/chvv  5.214s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: These benchmarks were run on a VirtualApple CPU (2.50GHz) using Go 1.21 on macOS. The numbers might vary slightly on different architectures, but the relative improvements should be similar.&lt;/p&gt;

&lt;p&gt;The results show some interesting insights:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Memory Usage&lt;/strong&gt;: The PoorLayout struct uses 32 bytes per instance, while the OptimizedLayout uses only 16 bytes. This means we save 16 bytes per struct instance, which is a 50% reduction in memory usage. For 1 million instances, this translates to 32MB vs 16MB - a massive difference that directly affects your application's memory footprint and garbage collection overhead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Allocation Performance&lt;/strong&gt;: The optimized version is 47% faster in allocation (447,033 ns/op vs 843,062 ns/op). This improvement comes from reduced memory pressure and better cache utilization during allocation. The CPU can process more optimized structs in the same time frame because they fit better in the cache hierarchy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Field Access Performance&lt;/strong&gt;: The optimized version shows a 55% improvement in field access speed (257,307 ns/op vs 567,032 ns/op). This significant performance gain comes from better cache locality and reduced cache line misses. Modern CPUs have a hierarchy of caches (L1, L2, L3) with different sizes and speeds:&lt;/p&gt;

&lt;p&gt;• L1 Cache: The fastest but smallest (typically 32-64KB per core)&lt;/p&gt;

&lt;p&gt;• L2 Cache: Medium speed and size (typically 256KB-1MB per core)&lt;/p&gt;

&lt;p&gt;• L3 Cache: The largest but slowest (typically 2-32MB shared)&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;optimized layout allows more structs to fit in these caches&lt;/strong&gt;, reducing the need to fetch data from main memory. When accessing fields in the poor layout, the CPU might need to load multiple cache lines due to padding, while the optimized layout can often fit multiple structs in a single cache line.&lt;/p&gt;

&lt;p&gt;The improvements are particularly significant in scenarios where you're dealing with large numbers of structs or performance-critical code paths. The combination of reduced memory usage and improved cache utilization can lead to substantial performance gains in real applications.&lt;/p&gt;

&lt;p&gt;Ok, but where does this make any difference? Some practical scenarios where these optimizations matter:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Web Servers&lt;/strong&gt;: A typical web server might handle thousands of concurrent requests, each potentially creating multiple structs for request processing, authentication, and response formatting. For example, if your server processes 10,000 requests per second, each creating 100 structs, that's 1 million structs per second - and the memory savings add up quickly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Data Processing&lt;/strong&gt;: When processing large datasets, you might load thousands of records into memory. For instance, a CSV file with 100,000 rows, each represented by a struct, would save 1.6MB of memory with optimized layout. This becomes even more significant when dealing with multiple concurrent data processing tasks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Game Development&lt;/strong&gt;: In game engines, you might have thousands of entities (players, NPCs, items) each represented by structs. The memory savings and performance improvements can be crucial for maintaining smooth gameplay, especially on resource-constrained devices.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. IoT Devices&lt;/strong&gt;: On resource-constrained devices, every byte counts. Optimizing struct layouts can help reduce memory usage and improve battery life by reducing the number of memory operations.&lt;/p&gt;

&lt;p&gt;In these scenarios, the cumulative effect of struct layout optimization can lead to:&lt;/p&gt;

&lt;p&gt;• Reduced memory pressure and fewer garbage collection cycles;&lt;/p&gt;

&lt;p&gt;• Better cache utilization and faster processing;&lt;/p&gt;

&lt;p&gt;• Lower resource requirements and better scalability;&lt;/p&gt;

&lt;p&gt;• Improved battery life on mobile and IoT devices;&lt;/p&gt;

&lt;p&gt;You can &lt;a href="https://gist.github.com/buarki/27824ef45d47e2a39795a9d8cfff9bb9" rel="noopener noreferrer"&gt;run the benchmark on your machine&lt;/a&gt; to see the actual numbers. The results might vary depending on your CPU architecture and Go version, but the relative differences should be similar.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Viztruct to Optimize Struct Layout
&lt;/h2&gt;

&lt;p&gt;While you can manually optimize struct layouts, tools like &lt;a href="https://viztruct.vercel.app/" rel="noopener noreferrer"&gt;viztruct&lt;/a&gt; can help visualize and optimize your struct layouts automatically. These tools can:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Show the actual memory layout of your structs&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Suggest optimal field ordering&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Calculate padding and alignment&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Help identify potential memory waste&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can use viztruct through its &lt;a href="https://viztruct.vercel.app/" rel="noopener noreferrer"&gt;web app&lt;/a&gt; or &lt;a href="https://github.com/buarki/viztruct?tab=readme-ov-file#cli-installation" rel="noopener noreferrer"&gt;install it locally&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go &lt;span class="nb"&gt;install &lt;/span&gt;github.com/buarki/viztruct/cmd/viztruct@latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Understanding and optimizing struct layout is an important aspect of writing efficient Go code. While the compiler handles most alignment automatically, being aware of these concepts can help you write more memory-efficient and performant code, especially in resource-constrained environments or when dealing with large numbers of structs.&lt;/p&gt;

&lt;p&gt;Remember that optimization should be guided by profiling and actual performance requirements. Not every struct needs to be perfectly optimized, and there are scenarios where struct padding should be "disabled", like in C networking code where you need precise control over memory layout for protocol headers. However, understanding these concepts will help you make informed decisions when performance matters.&lt;/p&gt;

</description>
      <category>go</category>
      <category>programming</category>
      <category>devops</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Searching Castles Using Go, MongoDB, Github Actions And Web Scraping</title>
      <dc:creator>Aurelio Buarque</dc:creator>
      <pubDate>Sun, 09 Jun 2024 18:42:28 +0000</pubDate>
      <link>https://dev.to/buarki/searching-castles-using-go-mongodb-github-actions-and-web-scraping-4h8j</link>
      <guid>https://dev.to/buarki/searching-castles-using-go-mongodb-github-actions-and-web-scraping-4h8j</guid>
      <description>&lt;h2&gt;
  
  
  It’s Been A While!
&lt;/h2&gt;

&lt;p&gt;It’s been a while since last one! TL;DR: work, life, study… you know :)&lt;/p&gt;

&lt;h2&gt;
  
  
  A Project Of Open Data Using Go
&lt;/h2&gt;

&lt;p&gt;In March 2024, I created a small project to experiment with using &lt;strong&gt;Server-Sent Events (SSE)&lt;/strong&gt; in a Go web server to continuously send data to a frontend client. It wasn’t anything particularly fancy, but it was still pretty cool :)&lt;/p&gt;

&lt;p&gt;The project involved a small server written in Go that served a minimalist frontend client created with raw HTML, vanilla JavaScript, and Tailwind CSS. Additionally, it provided an endpoint where the client could open an SSE connection. The basic goal was for the frontend to have a button that, once pressed, would trigger a server-side search to collect data about castles. As the castles were found, they would be sent from the server to the frontend in real-time. I focused on castles from the United Kingdom and Portugal, and the project worked nicely as you can see below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F40rdrtbbrdhcsskz5w07.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F40rdrtbbrdhcsskz5w07.gif" alt="Standalone version of Find castles working" width="600" height="351"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The code of such minimalist project can be found &lt;a href="https://github.com/buarki/find-castles/blob/10d0f8604011f0a98939b3fc4d70ccca4db6f401/cmd/standalone/main.go" rel="noopener noreferrer"&gt;here&lt;/a&gt; and you can follow the README instructions to run it on you local machine.&lt;/p&gt;

&lt;p&gt;A few days ago, I revisited this project and decided to expand it to include more countries. However, after several hours of searching, I couldn’t find an official consolidated dataset of castles in Europe. I did find a few datasets focused on specific countries, but none that were comprehensive. Therefore, for the sake of having fun with Go and because I have a passion for history, I started the project &lt;strong&gt;Find Castles&lt;/strong&gt;. The &lt;strong&gt;goal of this project is to create a comprehensive dataset of castles by collecting data from available sources, cleaning it, preparing it, and making it available via an API&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Go Really Shines For This Project?
&lt;/h2&gt;

&lt;p&gt;Goroutines and channels! The biggest part of the code of this project will be navigating through websites, collecting and processing data to, in the end, save it on database. By using Go we leverage the ease that the language offers us to implement these complex operations keeping the maximum possible amount of hair :)&lt;/p&gt;

&lt;h2&gt;
  
  
  How It Works So Far?
&lt;/h2&gt;

&lt;p&gt;So far I implemented data collectors for 3 countries only: &lt;strong&gt;Ireland, Portugal and United kingdom&lt;/strong&gt;, the reason was that the effort for finding a good reference for these countries was not so hard.&lt;/p&gt;

&lt;p&gt;The current implementation basically has two main stages: the website inspection for the links containing castle data and the data extraction per se. This process is the same for all countries and due to that an interface was introduced to establish an stable API for current and future enrichers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Enricher&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;CollectCastlesToEnrich&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="n"&gt;castle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="n"&gt;EnrichCastle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="n"&gt;castle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;castle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to see the implementation of at least one, &lt;a href="https://github.com/buarki/find-castles/blob/10d0f8604011f0a98939b3fc4d70ccca4db6f401/enricher/ireland.go" rel="noopener noreferrer"&gt;here you can find the enricher for Ireland&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once we have enrichers able to scrap and extract data from proper sources we can actually collect data using the &lt;strong&gt;executor package&lt;/strong&gt;. This package manages the execution of enrichers by leveraging goroutines and channels distributing the work load among the available CPUs.&lt;/p&gt;

&lt;p&gt;The executor current definition and function constructor can be see bellow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;EnchimentExecutor&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;enrichers&lt;/span&gt; &lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;castle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Country&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="n"&gt;enricher&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Enricher&lt;/span&gt;
    &lt;span class="n"&gt;cpus&lt;/span&gt;      &lt;span class="kt"&gt;int&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;cpusToUse&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;httpClient&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;enrichers&lt;/span&gt; &lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;castle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Country&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="n"&gt;enricher&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Enricher&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;EnchimentExecutor&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;cpus&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;cpusToUse&lt;/span&gt;
    &lt;span class="n"&gt;availableCPUs&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;runtime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NumCPU&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;cpusToUse&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;availableCPUs&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;cpus&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;availableCPUs&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;EnchimentExecutor&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;cpus&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;      &lt;span class="n"&gt;cpus&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;enrichers&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;enrichers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The execution process is basically a &lt;strong&gt;data pipeline&lt;/strong&gt; in which the first stage looks for castles to be enriched, next stage extract data from the given sources and last one persists it on DB.&lt;/p&gt;

&lt;p&gt;The first stage goes by spawning goroutines to find the castles and as those castles are found they are pushed into a channel. We then merge those channels into a single one to be consumed by the next stage:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;EnchimentExecutor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;collectCastles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="n"&gt;castle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;collectingChan&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="n"&gt;castle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;errChan&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;enricher&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;enrichers&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;castlesChan&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;castlesErrChan&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toChanel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;enricher&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;collectingChan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;collectingChan&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;castlesChan&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;errChan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;errChan&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;castlesErrChan&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;fanin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;collectingChan&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;fanin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;errChan&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;EnchimentExecutor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;toChanel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="n"&gt;enricher&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Enricher&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="n"&gt;castle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;castlesToEnrich&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="n"&gt;castle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;errChan&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="nb"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;castlesToEnrich&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="nb"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;errChan&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;englandCastles&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CollectCastlesToEnrich&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;errChan&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;englandCastles&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;castlesToEnrich&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;castlesToEnrich&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;errChan&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second stage spawn a group of goroutines to be listening to the output channel of previous stage, and as it receives castles it extracts data by scraping the HTML page. As the data extraction finishes, the enriched castles are pushed into another channel containing the enriched castles.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;EnchimentExecutor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;extractData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;castlesToEnrich&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="n"&gt;castle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="n"&gt;castle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;enrichedCastles&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="n"&gt;castle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;errChan&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="nb"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;enrichedCastles&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="nb"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;errChan&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;castleToEnrich&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;castlesToEnrich&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;enricher&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;enrichers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;castleToEnrich&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Country&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                    &lt;span class="n"&gt;enrichedCastle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;enricher&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EnrichCastle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;castleToEnrich&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="n"&gt;errChan&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="n"&gt;enrichedCastles&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;enrichedCastle&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="k"&gt;return&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}()&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;enrichedCastles&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;errChan&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the main executor’s function that does it all is bellow one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;EnchimentExecutor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Enrich&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="n"&gt;castle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;castlesToEnrich&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;errChan&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;collectCastles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;enrichedCastlesBuf&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="n"&gt;castle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="n"&gt;castlesEnrichmentErr&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;errChan&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cpus&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;receivedEnrichedCastlesChan&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;enrichErrs&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;extractData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;castlesToEnrich&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;enrichedCastlesBuf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;enrichedCastlesBuf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;receivedEnrichedCastlesChan&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;castlesEnrichmentErr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;castlesEnrichmentErr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;enrichErrs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;enrichedCastles&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;fanin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;enrichedCastlesBuf&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;enrichmentErrs&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;fanin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;castlesEnrichmentErr&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;enrichedCastles&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;enrichmentErrs&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The full current implementation of the executor can be found &lt;a href="https://github.com/buarki/find-castles/blob/10d0f8604011f0a98939b3fc4d70ccca4db6f401/executor/executor.go" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The last just consumes the channel with enriched castles and save them in bulk into MongoDB:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;castlesChan&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;errChan&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;castlesEnricher&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Enrich&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;buffer&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;castle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;castle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;castlesChan&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SaveCastles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;buffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;castle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;bufferSize&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SaveCastles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="n"&gt;buffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;errChan&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"error enriching castles: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can find the current version of the main.go &lt;a href="https://github.com/buarki/find-castles/blob/10d0f8604011f0a98939b3fc4d70ccca4db6f401/cmd/enricher/main.go" rel="noopener noreferrer"&gt;here&lt;/a&gt;. This process runs periodically using a scheduled job &lt;a href="https://github.com/buarki/find-castles/blob/10d0f8604011f0a98939b3fc4d70ccca4db6f401/.github/workflows/collect-and-enrich-castles.yml" rel="noopener noreferrer"&gt;created using Github Actions&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;This project has a considerable roadmap ahead, bellow you can find listed the next steps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Implement recursive crawling&lt;/strong&gt;: in order to add more enrichers is making it possible to do recursive crawling of a website, because some of them has a huge list of castles in such a way that the listing is done through pagination.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Support for multiple enrichment website sources for the same country&lt;/strong&gt;: It must also support multiple enrichment website sources of the same country because this is something possible as I could see.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Develop an official website&lt;/strong&gt;: In the meantime, an official website for this project must be done to make the collected data available and for sure to show the progress. Such site is in progress and you can already visit it here. Due to my lack of design skills the site is ugly as hell, but stay tuned and we’ll get over it :)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Integrate machine learning for filling data gaps&lt;/strong&gt;: And for sure, something that will help a lot, specially in complementing data hard to be found via the regular enrichers, will be machine learning, because by prompting these models with requests for hard-to-find data, we can efficiently fill in data gaps and enrich the dataset.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Contributions Are Welcome!&lt;/strong&gt;&lt;br&gt;
This project is open source and all &lt;strong&gt;collaborations are more than welcome&lt;/strong&gt;! Whether you’re interested in backend development, frontend design, or any other aspect of the project, your input is valuable.&lt;/p&gt;

&lt;p&gt;If you find anything you want to contribute — specially with frontend :) — just open an issue on the &lt;a href="https://github.com/buarki/find-castles" rel="noopener noreferrer"&gt;repository&lt;/a&gt; and ask for code review.&lt;/p&gt;

&lt;p&gt;This article was originally posted on my personal site: &lt;a href="https://www.buarki.com/blog/find-castles" rel="noopener noreferrer"&gt;https://www.buarki.com/blog/find-castles&lt;/a&gt;&lt;/p&gt;

</description>
      <category>go</category>
      <category>mongodb</category>
      <category>webscraping</category>
      <category>opendata</category>
    </item>
    <item>
      <title>Hexagonal Architecture/Ports And Adapters: Clarifying Key Concepts Using Go</title>
      <dc:creator>Aurelio Buarque</dc:creator>
      <pubDate>Thu, 21 Mar 2024 14:18:45 +0000</pubDate>
      <link>https://dev.to/buarki/hexagonal-architectureports-and-adapters-clarifying-key-concepts-using-go-14oo</link>
      <guid>https://dev.to/buarki/hexagonal-architectureports-and-adapters-clarifying-key-concepts-using-go-14oo</guid>
      <description>&lt;h2&gt;
  
  
  Disclaimer
&lt;/h2&gt;

&lt;p&gt;Before saying any words on such topic I must highlight some points: (1) The intent of such writing is providing a concrete, easy to understand and practical example of Hexagonal Architecture, due to that, the example is really simple to avoid a big cognitive load of peripheral topics. It’s really straight to the topic; (2) For sure there are several ways to approach the example shown bellow but this is just one simple and minimalist example with the intention of only pass the idea, you are more than welcome to share your thoughts as well :)&lt;/p&gt;

&lt;h2&gt;
  
  
  Motivation To Write this
&lt;/h2&gt;

&lt;p&gt;A few weeks ago, while chatting with a college newcomer in software development, I noticed she was struggling with some of the same difficulties I encountered when trying to understand Hexagonal Architecture. Latter on, looking for some &lt;a href="https://www.reddit.com/r/programming/comments/ovigjs/how_to_teach_ports_and_adapters/" rel="noopener noreferrer"&gt;discussions on reddit&lt;/a&gt; it seems this is something everyone faces, like this one:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgfd86jw369djyaqffuev.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgfd86jw369djyaqffuev.png" alt="A print from a reddis forum" width="714" height="106"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Due to that I decided to write down my 2 cents in this topic sharing what I know, what I’ve used and my experience in projects I’ve been involved in.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is Hexagonal Architecture? Where It Comes From? And Where Does the Hexagon Fit In?
&lt;/h2&gt;

&lt;p&gt;Being straightforward, &lt;strong&gt;Hexagonal Architecture has nothing to do with hexagons&lt;/strong&gt;. A better name for it is the one that the author, Alistair Cockburn, gave to it &lt;a href="https://alistair.cockburn.us/hexagonal-architecture/" rel="noopener noreferrer"&gt;in his blog post: Ports and Adapters&lt;/a&gt;. The name “Hexagonal Architecture” stuck probably due to the visual representation of the system’s structure that is usually used. So, as it has really nothing to do with hexagons, from now on let’s refer to the topic of this article by its proper name: Ports and Adapters :)&lt;/p&gt;

&lt;p&gt;Historically, Ports and Adapters was born in the context where Dependency Inversion Principle (DIP) was getting hot, back in the beginning of the 2000’s. DIP was getting more present on development day to day, and an example of a framework that was a pioneer in such topic is &lt;a href="https://github.com/google/guice/wiki/Guice10" rel="noopener noreferrer"&gt;Google Guice&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We can say that one of the first attempts to define a standard for software organization was &lt;a href="https://www.oreilly.com/library/view/software-architecture-patterns/9781491971437/ch01.html" rel="noopener noreferrer"&gt;N-Layered architecture&lt;/a&gt;. The basic idea is: group things related together. In practical terms it usually ends up with three layers: user interface, business logic and data access. The great leap N-Layared Architecture gave us was the &lt;strong&gt;separation from UI and business logic&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxgpmcvzcus2r6v61efjc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxgpmcvzcus2r6v61efjc.png" alt="Image describing n-layered" width="800" height="322"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It’s also important pointing that in 2003, two years before Alistair publishes his article, Eric Evans published his famous book &lt;strong&gt;Domain-Driven Design: Tackling Complexity in the Heart of Software&lt;/strong&gt; introducing DDD to the world. A core concept of DDD, which can be explored here in another article, is focusing on the &lt;strong&gt;business&lt;/strong&gt;, and we can refer to it using the concept of &lt;strong&gt;domain&lt;/strong&gt;. Again, DDD is a huge topic and I won’t be a fool to give the details of it only in one paragraph, but the outcomes it brings in terms of layers is the domain being the heart of system and a few layers besides it: the presentation layer, in charge of interacting with the client of the system (a person, another system etc); the application layer, which coordinates what needs to be done; and the infrastructure layer, which is in charge of DBs, notifications etc. Bellow image might help to illustrate the idea.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzg58ubnxkhpn736io8ep.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzg58ubnxkhpn736io8ep.png" alt="DDD idea" width="720" height="227"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A practical description of the image above is as follows: the presentation uses the application, the application uses the domain, and the domain uses the infrastructure. In the past (around the early 2000s), the DIP wasn’t as obvious as it is today (2024), as result, the layers used to reference the next one downstream. This means that, for instance, the domain layer could have references to the database details (I’m not pointing it here as something good or bad).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The idea of Ports and Adapters was introduced to address this because it enforces DIP to isolate the domain from details not directly involved in the business&lt;/strong&gt;, like the application and infrastructure layers. This is done by &lt;strong&gt;reversing the dependency relation&lt;/strong&gt; of the three layers shown bellow where the domain defines a contract of how it needs things to work and the peripheral layers will take care of following this contract. This contract is named &lt;strong&gt;Port&lt;/strong&gt;, and an implementation of a Port is named &lt;strong&gt;Adapter&lt;/strong&gt;. And other important name to mention here is that the “business part” is named &lt;strong&gt;Core&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That way, we kind of get rid of application and infrastructure layers per se and now we are relying on contracts defined by the core, which could be an interface in the world of Object Oriented Programming (OOO), and adapters of the surrounding layers implementing such contracts, like classes in OOO. As you can imagine, there will be adapters that will trigger actions or use the core, and these ones are called &lt;strong&gt;primary adapters&lt;/strong&gt;. And the adapters called/triggered by the core are called &lt;strong&gt;secondary adapters&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;To help you visualize it, rather than just show an image, image an application that has a web API and an AMQP event listener that can dispatch business processes, and based on some requirements it must persist something on DB and send email to user. On such hypothetical app, we could say that the core of the application would need to provide four ports: one for the web API, one for the AMQP listener, one to define how to send emails and one to define how to interact with DB. Assuming that the web API will use REST, the AMQP listener will be based on RabbitMQ, the emails will be sent using SendGrid and the DB will be Mongo, we could have the following configuration:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpgqvxxz6nqxeswp8afah.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpgqvxxz6nqxeswp8afah.png" alt="Example of Ports and Adapters app" width="720" height="287"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can imagine, primary adapters usually take the form of REST controllers, event listeners (such as for Kafka or RabbitMQ), or Command Line Interfaces (CLIs). These are the components that trigger actions or use the core functionalities of the application.&lt;/p&gt;

&lt;p&gt;On the other hand, secondary adapters typically include well-known components like repositories, SMTP clients, and services implementing storage management, such as an AWS S3 client. These adapters are the ones called or triggered by the code, serving as bridges between the application’s core and external systems or data storage.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Short But Effective Example
&lt;/h2&gt;

&lt;p&gt;The following example will show a concrete example using Go.&lt;/p&gt;

&lt;p&gt;Consider an hypothetical and isolated application feature which is: the user must opt-in for receiving news, maybe from a news website, and once it opts in it must receive an email confirming it. Easy and minimalist as is. Consider that the "service" that executes it, not compliant with Ports and Adapters yet, is this one and also that this is triggered from a POST request (let's abstract this part or the example will be too big):&lt;br&gt;
&lt;/p&gt;

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

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

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;NewsSubscriber&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;emailSender&lt;/span&gt; &lt;span class="n"&gt;sendgrid&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Mailer&lt;/span&gt;
    &lt;span class="n"&gt;newsDB&lt;/span&gt;      &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Connection&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;NewsSubscriber&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;NewsSubscriber&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;emailSender&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;sendgrid&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;newsDB&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;      &lt;span class="n"&gt;postgres&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewConnection&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ns&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;NewsSubscriber&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;sqlQuery&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;`
      UPDATE
        users 
      SET
        receive_news = 1
      WHERE
        id = %s`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;ns&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;newsDB&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sqlQuery&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"failed to subscribe user to updates"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;sendgrid&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Email&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Subject&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Subscription successfully done!"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="s"&gt;"Now you are subscribed to receive updates"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;      &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;ns&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;emailSender&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"failed to send email to user"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Above code is kind of a crime, but take a deep breath and will get over it :)&lt;/p&gt;

&lt;p&gt;In order to make it compliant with Ports and Adapter we can start pointing what is not compliant. If we take a close look at the New() function we see it is creating the instances of the email service and postgres connection client inline. So the first thing we can highlight is that we have a strong coupling between this service and the provider to send emails and the postgres database.&lt;/p&gt;

&lt;p&gt;Other point is that the method &lt;em&gt;Subscribe&lt;/em&gt; is directly creating the SQL statement to mark the user's row as "I want to receive news", again a feature of system's core is knowing too much about an implementation detail.&lt;/p&gt;

&lt;p&gt;With above points we can see how hard it is to unit test this feature is. We also see that if, for instance, the company needs to replace Sendgrid with Mailgun, the feature must be directly modified to achieve it. Now imagine that the database also needs to be changed for MongoDB, another gigantic refactoring would be needed. &lt;strong&gt;Again, this is just an hypothetical and drastic scenario to illustrate the idea, for sure such examples don't happen quite often in real life, keep calm :)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In order to make it compliant with Ports and Adapters we could introduce two Ports: EmailSender and NewsSubscriptionRegister. Both them are interfaces describing &lt;strong&gt;what&lt;/strong&gt; our service NewsSubscriber needs, and the "how" it is done does not matter. They are:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;EmailParams&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Subject&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="n"&gt;Body&lt;/span&gt;    &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="n"&gt;To&lt;/span&gt;      &lt;span class="kt"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// Port defining WHAT the email sender should do&lt;/span&gt;
&lt;span class="c"&gt;// and not HOW.&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;EmailSender&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="n"&gt;EmailParams&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Port defining WHAT the process of saving on&lt;/span&gt;
&lt;span class="c"&gt;// DB that user wants to receive news should do&lt;/span&gt;
&lt;span class="c"&gt;// and not HOW&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;NewsSubscriptionRegister&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can now adjust the &lt;em&gt;NewsSubscriber&lt;/em&gt; to &lt;strong&gt;depends of such Ports:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;NewsSubscriber&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;emailSender&lt;/span&gt;               &lt;span class="n"&gt;EmailSender&lt;/span&gt;
    &lt;span class="n"&gt;newsSubscriptionRegister&lt;/span&gt;  &lt;span class="n"&gt;NewsSubscriptionRegister&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By doing so &lt;strong&gt;we applied the Dependency Inversion Principle (DIP)&lt;/strong&gt; and now the service is depending on a contract that fits its needs, without any worries about how it works. Not directly related to it, but we could go even further enhancing flexibility and modularity by applying the &lt;strong&gt;Inversion Of Control (IOC)&lt;/strong&gt; principle in such scenarion by using &lt;strong&gt;dependency injection&lt;/strong&gt; in the New constructor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;emailSender&lt;/span&gt; &lt;span class="n"&gt;EmailSender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;newsSubscriptionRegister&lt;/span&gt; &lt;span class="n"&gt;NewsSubscriptionRegister&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;NewsSubscriber&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;NewsSubscriber&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;emailSender&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;               &lt;span class="n"&gt;emailSender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;newsSubscriptionRegister&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;  &lt;span class="n"&gt;newsSubscriptionRegister&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With above modification we removed the coupling that a Core feature had with the details of sending emails and database persistence by introducing contracts (Ports) that meets our needs and provides a clear separation of concerns. Additionally, we also are no longer in charge of the instantiation of those contracts implementations, the Adapters. Instead, we are delegating it the the client of the service &lt;em&gt;NewsSubscriber&lt;/em&gt;. And the Register implementation might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ns&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;NewsSubscriber&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;ns&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;newsSubscriptionRegister&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;EmailParams&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Subject&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Subscription successfully done!"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="s"&gt;"Now you are subscribed to receive updates"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;      &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;ns&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;emailSender&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this implementation, the &lt;em&gt;NewsSubscriber&lt;/em&gt; service is no longer directly responsible for the database registration. Instead, it delegates this responsibility to the &lt;em&gt;newsSubscriptionRegister&lt;/em&gt; instance, providing a cleaner and more modular design. The Register method, representing the database registration action, encapsulates the specific logic related to news subscription registration.&lt;/p&gt;

&lt;p&gt;About the adapters implementation we could have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;SendGridEmailSender&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="n"&gt;sendgrid&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Mailer&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;SendGridEmailSender&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="n"&gt;core&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EmailParams&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"failed to send email to user, got %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And also:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;PostgresNewsSubscriptionRegister&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;connection&lt;/span&gt; &lt;span class="n"&gt;postgres&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Connection&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;PostgresNewsSubscriptionRegister&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="n"&gt;core&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;sqlQuery&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;`
      UPDATE
        users 
      SET
        receive_news = 1
      WHERE
        id = %s`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sqlQuery&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"failed to subscribe user to updates, got %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Outcomes Of Using Ports And Adapters
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Note: I didn't use benefits or drawbacks as title and I'll elaborate the reason soon.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Adopting Ports and Adapters brings a considerable &lt;strong&gt;flexibility to replace components and providers&lt;/strong&gt;. As an example, if the service you use to send emails gets expensive and the CTO of your company made a deal with a cheaper provider, the replacement should not be so hard as the main work would basically be, at least in theory, just implementing a new component respecting the Port contract.&lt;/p&gt;

&lt;p&gt;Another trait of a code using Ports and Adapters is the &lt;strong&gt;high level of testability&lt;/strong&gt;, because as the Core only deals with contracts, creating unit tests is a rather easy tasks using software doubles, like mocks.&lt;/p&gt;

&lt;p&gt;Probably the most widely mentioned trait is &lt;strong&gt;completely isolation of frameworks and libraries&lt;/strong&gt;. This can be achieved by integrating the framework or lib through adapters, ensuring that the Core remains completely agnostic to such points.&lt;/p&gt;

&lt;p&gt;By using Ports and Adapters you also get &lt;strong&gt;reduced risk of vendor lock-in&lt;/strong&gt;, as the first example given above has shown: if a provider is no longer suitable the Core of system will not be highly coupled to it, so a replacement should not be so complex.&lt;/p&gt;

&lt;p&gt;Something really worth pointing out is the &lt;strong&gt;potential misuse and overhead of layers&lt;/strong&gt;. The adoption of Ports and Adapters could potentially introduce the risk of developers getting excited on creating not necessary layers using as argument some of the above points. Personally, this is something I could see in the majority of systems I worked on using it, and a clear result of such layers misuse was the Pull Request size for a simple modification, like adding a new method to a Port, spanning across multiple files and directories.&lt;/p&gt;

&lt;p&gt;And last, but not least, two things that must be mentioned are the &lt;strong&gt;learning curve and the initial development type&lt;/strong&gt;. Trust me, do not neglect these topics, especially if the team that will be working with it is not familiar. It's important to give the team time to get used to it so they can gain traction.&lt;/p&gt;

&lt;p&gt;I named above points as outcomes rather than "benefits and drawbacks" because &lt;strong&gt;they can be labeled as benefits or drawbacks only with context&lt;/strong&gt;. Is having the core of the business 100% decoupled from the framework a must for your business? If so, then go for it. The point is: &lt;strong&gt;don't adopt Ports and Adapters blindly just because other teams or companies are using it&lt;/strong&gt;. Look for your needs, be straight to them and check if the intrinsic cost of effort of Ports and Adapters worth once compared to the outcomes of it to the project. I have worked in projects where Port and Adapters was a nice fit (and AFAIK the system is still in use after years), but I also have faced situations where a simple transaction script could do the job, but instead, it was done using a pile of layers in which the effective code, if placed in a single file, would not have more than +180 lines :)&lt;/p&gt;

&lt;p&gt;Ports and Adapters is a valuable technique, not a strict doctrine. It is up to us to apply our skills, experience, and wisdom to make informed decisions on whether to embrace this architectural pattern based on our project's specific needs.&lt;/p&gt;

&lt;p&gt;If you like this topic, I have also an article comparing Hexagonal Architecture with other famous software design buzz words, like Clean Architecture, you can check it &lt;a href="https://www.buarki.com/blog/onion-cleanarch-hexagonal" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>cleancode</category>
      <category>programming</category>
      <category>architecture</category>
      <category>go</category>
    </item>
    <item>
      <title>Building A File Compressor Using C And Wasm</title>
      <dc:creator>Aurelio Buarque</dc:creator>
      <pubDate>Wed, 21 Feb 2024 01:45:16 +0000</pubDate>
      <link>https://dev.to/buarki/building-a-file-compressor-using-c-and-wasm-289d</link>
      <guid>https://dev.to/buarki/building-a-file-compressor-using-c-and-wasm-289d</guid>
      <description>&lt;h2&gt;
  
  
  Checking Wasm Tools For C
&lt;/h2&gt;

&lt;p&gt;In a &lt;a href="https://www.buarki.com/blog/webassembly" rel="noopener noreferrer"&gt;previous article&lt;/a&gt; I did a small experiment with Go and Wasm to check the state of tools available for it in 2024. During such endeavour I find a tool called &lt;a href="https://emscripten.org/docs/getting_started/Tutorial.html" rel="noopener noreferrer"&gt;Emscripten&lt;/a&gt; that drawn my attention and made my mind to do one experiment, now using C.&lt;/p&gt;

&lt;p&gt;Emscripten is an Open Source compiler toolchain to WebAssembly. As its docs says, practically any portable C or C++ codebase can be compiled into Wasm using it.&lt;/p&gt;

&lt;p&gt;With the tool to use defined next step was defining a problem to approach.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem To Solve: Compress Files Using Huffman Algorithm
&lt;/h2&gt;

&lt;p&gt;I must be honest, C is boo of mine ❤! Due to that, I really wanted to build something real that could make me do some bit fiddling, and I decided to implement a very interesting compression algorithm called Huffman Coding.&lt;/p&gt;

&lt;p&gt;Huffman Coding was &lt;a href="https://en.wikipedia.org/wiki/Huffman_coding#:~:text=In%20computer%20science%20and%20information,while%20he%20was%20a%20Sc." rel="noopener noreferrer"&gt;created by David A. Huffman back in 1952&lt;/a&gt;. It is a lossless compression method, which means that all bits present on original file will be carried to the decompressed one. The basic idea of the algorithm is to collect all the symbols present on a file besides its frequency to create a binary tree in which all symbols will be leaves and the path from the tree root to the leaf should be used to represent the symbol. I know that at first glance it looks like rocket science, but trust me, it is far away from that and in this one we will see it in baby steps :)&lt;/p&gt;

&lt;p&gt;This project was something really nice doing and I hope you enjoy following up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Non-Goals Of This Project And Its Limitations
&lt;/h2&gt;

&lt;p&gt;This project was not intended to be a production ready file compressor like WinRAR. It also was not intended to support files bigger than 150MB to avoid memory issues on user browser. For sure it can be adjusted, but I guess this size is fine for a hobby project :)&lt;/p&gt;

&lt;h2&gt;
  
  
  How Can Use The Program?
&lt;/h2&gt;

&lt;p&gt;To use the program, zipper as I named it, &lt;a href="https://zipper-zeta.vercel.app/" rel="noopener noreferrer"&gt;just access it&lt;/a&gt;, and in case you want to check the details, the full code it is &lt;a href="https://github.com/buarki/zipper" rel="noopener noreferrer"&gt;available on my Github&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Explaining Huffman Coding In Baby Steps
&lt;/h2&gt;

&lt;p&gt;There’s no better way to understand something rather than a practical example, so let’s apply the algorithm with the following text: &lt;em&gt;coding is fun and fun is coding&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First step: collect all the symbols besides its frequencies&lt;/strong&gt;. By doing so we get:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0w6dc86igxqxifluql0v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0w6dc86igxqxifluql0v.png" alt="All symbols collected" width="237" height="726"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Second step: sort collected symbols by its frequencies&lt;/strong&gt;. The output in ascending order from top to the bottom is:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn0xyn0u5wkg0by3bysew.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn0xyn0u5wkg0by3bysew.png" alt="Symbols sorted" width="282" height="869"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Third step: create a binary tree from this sorted list. Do so by removing the two least frequenct symbols and creating a node tree with them, in which the less frequent symbol goes to the left and the most frequent to the right. And also, the “frequency” of this new node is the sum of the subtree frequencies. Put this new created node into that list and repeat this process until the list have only one item&lt;/strong&gt;. Keep calm! It seems more complex than the portuguese grammar but trust me, it is not. Let’s just execute this algorithm and you’ll agree with me.&lt;/p&gt;

&lt;p&gt;It says, “remove the two least frequent symbols”. The two least frequent symbols are &lt;em&gt;a&lt;/em&gt; and &lt;em&gt;c&lt;/em&gt;, so let’s remove them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj11xw0oo8mk1kgidofx3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj11xw0oo8mk1kgidofx3.png" alt="First two nodes" width="334" height="464"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then it says “create a tree node where the least frequent symbol goes to the left and most frequent to the right. And the frequency of this new node is the sum of the subtrees”. In order to have a way to distinguish the regular file symbols to this “joining” symbol let’s adopt the @ as the joining symbol. The result of such process is this one:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw45mcwk42v1ml2uny5on.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw45mcwk42v1ml2uny5on.png" alt="Building the tree" width="384" height="322"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the new node is built we must put it back to the list. &lt;strong&gt;As the list is sorted we should keep it sorted while adding it&lt;/strong&gt;. The list items, arranged horizontally to help you visualize it, looks like this now:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjzt42oj8t5hh0xk92n01.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjzt42oj8t5hh0xk92n01.png" alt="Building the tree" width="720" height="99"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we just repeat the process, until only one item remains on the list. The next two items to process are f and g. The new tree node created from them is this one:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Falo794muxakomb8caiqi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Falo794muxakomb8caiqi.png" alt="Building the tree" width="384" height="323"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Adding it to the list we get:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqkh8li9eeidpvyxv76xi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqkh8li9eeidpvyxv76xi.png" alt="Building the tree" width="720" height="103"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The next two items to process are o and o. The new tree node created from them is this one:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftpxuisprq2q4y2bddaq5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftpxuisprq2q4y2bddaq5.png" alt="Building the tree" width="384" height="300"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Adding it to the list we get:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv86oxk9e83fq1kgd46jv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv86oxk9e83fq1kgd46jv.png" alt="Building the tree" width="720" height="112"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The next two items to process are u and the joining symbol @:3. The new tree node created from them is this one:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8pfb9pxqdklnj7moozfa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8pfb9pxqdklnj7moozfa.png" alt="Building the tree" width="384" height="341"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Adding it to the list we get:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh8hb3goyzgr1s4b6yh07.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh8hb3goyzgr1s4b6yh07.png" alt="Building the tree" width="720" height="191"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The next two items to process are d and the first joining symbol @:4. The new tree node created from them is this one:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyqnb2o45rmfdr79njb5l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyqnb2o45rmfdr79njb5l.png" alt="Building the tree" width="384" height="357"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Adding it to the list we get:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz92fi73o6zwc7l2z4bs8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz92fi73o6zwc7l2z4bs8.png" alt="Building the tree" width="720" height="213"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The next two items to process are @:4 and the joining symbol i:4. The new tree node created from them is this one:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fktfsvnqt0fe3nsn8vail.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fktfsvnqt0fe3nsn8vail.png" alt="Building the tree" width="384" height="314"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Adding it to the list we get:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr7lpl1jsd3av2eflb8m1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr7lpl1jsd3av2eflb8m1.png" alt="Building the tree" width="720" height="192"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The next two items to process are @:5 and n:5. The new tree node created from them is this one:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frm96xdv91q3hnwd5g2gb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frm96xdv91q3hnwd5g2gb.png" alt="Building the tree" width="384" height="445"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Adding it to the list we get:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F128otsammcvpj29zzka2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F128otsammcvpj29zzka2.png" alt="Building the tree" width="720" height="265"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The next two items to process are &lt;em&gt;space&lt;/em&gt;:6 and @:7. The new tree node created from them is this one:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9d6l4ryvz4xzj8ags8ha.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9d6l4ryvz4xzj8ags8ha.png" alt="Building the tree" width="384" height="318"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Adding it to the list we get:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy2fi2ks710nq97a1m20g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy2fi2ks710nq97a1m20g.png" alt="Building the tree" width="720" height="256"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The next two items to process are @:8 and @:10. The new tree node created from them is this one:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fahv2jv3t0qtwsanxfc2b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fahv2jv3t0qtwsanxfc2b.png" alt="Building the tree" width="384" height="290"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Adding it to the list we get:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhwj425645zx1thsn74ps.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhwj425645zx1thsn74ps.png" alt="Building the tree" width="720" height="318"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The last two items to process are @:10 and @:18. &lt;strong&gt;The new tree node created is the complete Huffman tree:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu3xeovjwkxn0pq76bqoy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu3xeovjwkxn0pq76bqoy.png" alt="Building the tree" width="720" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fourth step: find the corresponding symbol code by walking the tree from the root to the leaf and use 0 to mark a jump to the left and 1 to the right&lt;/strong&gt;. Adding the 0s and 1s we get this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh6qt5dw0eroaja29egzb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh6qt5dw0eroaja29egzb.png" alt="Codes" width="720" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thus, the corresponding codes are:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9irgvkrbwtgbsy74m9tm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9irgvkrbwtgbsy74m9tm.png" alt="Table codes" width="263" height="740"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It worth noticing that that &lt;strong&gt;the higher the frequency of a symbol, the smaller the corresponding code length is&lt;/strong&gt;. Just compare the length of &lt;strong&gt;space&lt;/strong&gt; and a.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fifth step: Replace the symbol for its corresponding code&lt;/strong&gt;. If we rewrite the present words using found codes we get:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgxied75ukn98tw74fogu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgxied75ukn98tw74fogu.png" alt="Table codes" width="472" height="634"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thus, the full text coding is fun and fun is coding is: &lt;em&gt;11011 0110 010 101 111 1001 00 101 0111 00 1000 1100 111 00 11010 111 010 00 1000 1100 111 00 101 0111 00 1011 0110 010 101 111 1001.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;As &lt;a href="https://www.ascii-code.com/faq#:~:text=ASCII%20was%20originally,character%20encoding%20standard." rel="noopener noreferrer"&gt;ASCII characters uses 1 byte&lt;/a&gt;, the original text coding is fun and fun is coding requires 31 (length of text) x 1 byte = 31 bytes to be stored. On the other hand, the compressed version requires only 12 bytes and 6 bits of one extra byte, so in total 13 bytes. To visualize it, just group the 0s and 1s in chunks of 8 (because 1 byte is a group of 8 bits): &lt;em&gt;11011011 00101011 11100100 10101110 01000110 01110011 01011101 00010001 10011100 10101110 01011011 00101011 111001.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;With simple math we can see that &lt;strong&gt;the compressed content is almost 58% smaller than the original one!&lt;/strong&gt; This simple example shows how powerful Huffman Coding is.&lt;/p&gt;

&lt;p&gt;Once you became a Huffman Coding master we can proceed to next step :)&lt;/p&gt;

&lt;h2&gt;
  
  
  The (Funny) C Part Of Compression
&lt;/h2&gt;

&lt;p&gt;In order to elaborate the details of the compression part we must define the &lt;strong&gt;Compression API&lt;/strong&gt; written in C that will be used on the JavaScript layer through Wasm. &lt;a href="https://github.com/buarki/zipper/blob/master/huffman/compress.h" rel="noopener noreferrer"&gt;The compression header file can be seen here&lt;/a&gt; and as we can see it expects the full file content to be passed alongside with its size in bytes. Such API imposes some limitations, like the size of the file we can handle, because it would not be feasible loading a 2GB file into an unsigned char buffer, but that’s totally ok for this project as &lt;strong&gt;it was not intending to be a production ready compressor like WinRAR ;)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That said, we can go on with compression details. As shown above, we start by &lt;strong&gt;collecting the symbols&lt;/strong&gt; with its frequencies, and the &lt;a href="https://github.com/buarki/zipper/blob/master/huffman/collect_bytes_frequency.c" rel="noopener noreferrer"&gt;implementation of it can be found here&lt;/a&gt;. The idea is creating an array with 256 buckets, each one representing the frequency of the corresponding ASCII symbol in the file. Thus, the frequency of symbol ‘a’ would be stored at index 97. It worth mentioning it uses &lt;strong&gt;calloc&lt;/strong&gt; to create such array, to ensure that all buckets have zero values once created, &lt;strong&gt;otherwise we could have some “trash” on such buckets, which could lead to bugs during this stage.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;With the frequencies of symbols collected, we can now sort them&lt;/strong&gt;. To do so, I decided to use a &lt;strong&gt;Min Heap data structure&lt;/strong&gt; as it is easy to find the lower value present inside of it. As we are always getting the two lowest values, using such data structure seems a good design decision. I implemented the Min Heap used on this project, and you can find the &lt;a href="https://github.com/buarki/zipper/blob/master/huffman/min_heap.h" rel="noopener noreferrer"&gt;Abstract Data Type (ADT) of it here.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Creating the Min Heap from the array with symbols frequency is a straightforward process and the implementation &lt;a href="https://github.com/buarki/zipper/blob/master/huffman/build_min_heap_from_bytes_frequency.c" rel="noopener noreferrer"&gt;can be seen here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once we have the Min Heap ready, we can now &lt;strong&gt;build the Huffman tree&lt;/strong&gt;. You can find the &lt;a href="https://github.com/buarki/zipper/blob/master/huffman/build_huffman_tree_from_min_heap.c" rel="noopener noreferrer"&gt;implementation of such process here&lt;/a&gt; and also the &lt;a href="https://github.com/buarki/zipper/blob/master/huffman/huffman_tree.h" rel="noopener noreferrer"&gt;Huffman Tree ADT&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;From the Huffman Coding execution we did above, the last step missing is &lt;strong&gt;building the Huffman table with the codes&lt;/strong&gt;. You can find the &lt;a href="https://github.com/buarki/zipper/blob/master/huffman/build_symbol_codes_from_tree.c" rel="noopener noreferrer"&gt;implementation of it here&lt;/a&gt;. The table itself is a 256x256 2D array of type unsigned char, where the &lt;strong&gt;table[i]&lt;/strong&gt; represents the code of the a symbol present in the file. For instance, if the symbol &lt;strong&gt;‘a’&lt;/strong&gt; is present and its code turns out to be &lt;strong&gt;1101&lt;/strong&gt; it means that the &lt;strong&gt;table[97]&lt;/strong&gt; will carry the string &lt;strong&gt;‘1’, ‘1’, ‘0’, ‘1’, ‘\0’&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;All above points is all we need to implement Huffman Coding, but that’s not all yet. Because in order to decompress the compressed file, the sequence of 0s and 1s, we need the tree. &lt;strong&gt;Thus, to give the compressed file to someone expecting to decompress it, the tree must be sent together, and it introduces a nice problem to solve.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Architecture Of The Compressed File
&lt;/h2&gt;

&lt;p&gt;Let’s assume that with the tree and the codes table on hand we get the 0s and 1s of the compressed file. Now we want to give the compressed content to a friend besides the tree to it knows how to decompress the file. &lt;strong&gt;How should we pass the tree? With The symbols traversed in some order? maybe postorder? And should we pass the tree content before or after the 1s and 0s?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;There’s no right or wrong answer for above questions, but the one picked must be very well implemented. For this project, &lt;strong&gt;I decided to build the “file that we share” with the following structure: the contents of the tree traversed in preorder and right after it the 0s and 1s&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Traversing a tree in preorder means, in simple terms, that you’ll collect the symbol of a node, look for the left subtree and then to the right subtree. A concrete example of it, using the tree we created for the text coding is fun and fun is coding is: &lt;em&gt;@@_space_@d@os@@@fgi@@u@acn&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;That said, the “file we share” with the compressed file “would be”: &lt;em&gt;@@_space_@d@os@@@fgi@@u@acn 11011011 00101011 11100100 10101110 01000110 01110011 01011101 00010001 10011100 10101110 01011011 00101011 111001&lt;/em&gt;. Ok, now we defined the basic layout of the file that is sent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But how the one that has received the compressed file would know where the content of tree ends?&lt;/strong&gt; Different files would have different trees and consequently different series of 0s and 1s, so &lt;strong&gt;we need a strategy to inform to the receiver where the tree content and the 0s and 1s starts and ends&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In order to the receiver knows how to properly find the tree and the compressed content we need to add a header to the sent file&lt;/strong&gt;.You can think about the header as some special bytes at the beginning of the file that will provide some information about it. For this project, it must inform how to properly find the content of the tree and the compressed files separately. &lt;strong&gt;A header must be well planned because it inevitably increases the final size of the file shared&lt;/strong&gt;. Let’s see how we built the header for this project by addressing the tree content size first.&lt;/p&gt;

&lt;p&gt;Using above example of file that could be shared, the one attempting to decompress it must be able to find properly &lt;em&gt;@@_space_@d@os@@@fgi@@u@acn&lt;/em&gt; as the content of the tree. In such example we see that the tree content uses 27 bytes. &lt;strong&gt;It’s important pointing that by tree size I mean the amount of nodes on the tree, not the height&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Counting the number of nodes on the tree is a rather easy task, and the &lt;a href="https://github.com/buarki/zipper/blob/master/huffman/huffman_tree.c#L35" rel="noopener noreferrer"&gt;implementation can be seen here&lt;/a&gt;. As we know that the Huffman tree is a binary tree, if a tree completely uses all 256 ASCII symbols, we can say it would have (2 * 256) — 1 = 511 nodes. But for this project this equation requires one small adjustment. If you remember the execution we did above, you’ll notice that for this project we used the symbol @ as the “joining symbol”. The problem is that a file can also have this symbol in its content, due to that we need a way to distinguish when we are dealing the the @ of the joining symbol and the @ of the file per se.&lt;/p&gt;

&lt;p&gt;The strategy adopted here was using the symbol \ as “escaping”. But the same problem goes for it, so we need to use \ not only for the @ but also for the \. The implication of it is that, if @ or \ are present we would require 2 additional bytes, or “fake nodes”, to comport it. Thus, for the worst case scenario, &lt;strong&gt;the maximum number of nodes on the tree would be ((2 * leaves) — 1) + 2, thus, 513 nodes&lt;/strong&gt;. Such number in binary is 1000000001, which means it requires 1 byte and 2 additional bits to be stored.&lt;/p&gt;

&lt;p&gt;So far, we know that we need two additional bytes on the “compressed file” to represent the size of tree content (two because 10 bits is bigger than 8 bits, one byte, so we need one extra full byte to comport it). But, even if we put the information of tree size in the header there is still one more problem to be addressed: &lt;strong&gt;how to know how many bits the compressed part exactly uses?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If we look again to the content of compressed file, 11011011 00101011 11100100 10101110 01000110 01110011 01011101 00010001 10011100 10101110 01011011 00101011 111001, we can see it uses 12 complete bytes with one extra byte that uses only 6 bits. Thus, even if we know where the content of the compressed file starts, the number of bytes required by the tree + 1, we must know exactly how many bits do we need to look for, especially on the last byte to skip the &lt;strong&gt;padding bits.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Padding bits are nothing but bits used to “complete” the byte. It happens because a byte is a sequence of 8 bits, even if we use less. Check bellow image to visualize it:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2tgm15t8c0e92p3oo9fq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2tgm15t8c0e92p3oo9fq.png" alt="Padding example" width="720" height="341"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The good part is that calculating how many padding bits we would have is easy. To do so, we need to first compute the “length” of the compressed content, in other other words, the length of 0s and 1s. This is done by summing all the products (symbol frequency * respective code length) and you can see the &lt;a href="https://github.com/buarki/zipper/blob/master/huffman/compute_compressed_file_symbols_length.c" rel="noopener noreferrer"&gt;implementation here&lt;/a&gt;. With the length of compressed content on hand, we just need to check the remainder of it with 8 bits to know how many bits are being used, and then, we check how many bits are missing to complete 8 bits, in other others: 8 - (compressed content length % 8). You can see the the &lt;a href="https://github.com/buarki/zipper/blob/master/huffman/compute_padding_bits_for_compressed_codes.c" rel="noopener noreferrer"&gt;implementation here&lt;/a&gt;.The text coding is fun and fun is coding has 102 bits length, so 8 — (102 % 8) = 2 padding bits :)&lt;/p&gt;

&lt;p&gt;Unless that the last byte is fully used, the max number of padding bits will be 7, and it can be represented using 3 bits: 111.&lt;/p&gt;

&lt;p&gt;Thus, we got a puzzle to solve: we have two information to store in the header, the tree content size, which requires at most 10 bits, and the number of padding bits, that requires at most 3 bits, resulting in 13 bits for the header, and consequently, 2 bytes. Due to the importance of the header in this project I decided to define an &lt;a href="https://github.com/buarki/zipper/blob/master/huffman/header.h" rel="noopener noreferrer"&gt;ADT for it to highlight its API&lt;/a&gt;. The solution adopted for the header puzzle was: &lt;strong&gt;the 3 first bits of the header first byte will store the padding bits, and the last 2 bits of first byte besides the entire second byte will store the tree content size&lt;/strong&gt;. For the text coding is fun and fun is coding the padding is 2, thus 10 in binary, and the tree size is 27, or 11011. Bellow image illustrates how the header bits are used:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fahzw1c3qsmu5uqligquh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fahzw1c3qsmu5uqligquh.png" alt="Header" width="720" height="384"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Building the header like above is pure bit fiddling, and is done like this: we create a byte where the 3 bits of padding are placed at the most significant bits by doing a left shift of 5 bits. If the padding for the text example is 2 (2 in 8 bits is 00000010), 2 &amp;lt;&amp;lt; 5 will gives us 01000000. Then, we collect the most significant bytes of tree content size. In our example, the size is 27, or 0000000000011011 in binary using 2 bytes. To collect the most significant bytes we do a right shift of 8 bits like 0000000000011011 &amp;gt;&amp;gt; 8 and we get 00000000. We also need to collect the least significant bits of tree size, we do it executing a bitwise AND with the tree size and a full set byte: 0000000000011011 &amp;amp; 11111111 and we get 00011011. The last step is creating a 2 bytes sized buffer to carry the header, where the first byte will have the result of the byte with the 3 bits of padding at the most significant bits bitwise OR the most significant bits of tree size, in our case 01000000 | 00000000 = 01000000. And the second byte will just receive the least significant bits of the tree size, which is 00011011. The final result is 0100000000011011. You can check the &lt;a href="https://github.com/buarki/zipper/blob/master/huffman/header.c#L56" rel="noopener noreferrer"&gt;implementation of this step here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And we finally have the three parts of the file:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faus1qtykcarctx7w4vd1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faus1qtykcarctx7w4vd1.png" alt="All content" width="720" height="85"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The full compression implementation &lt;a href="https://github.com/buarki/zipper/blob/master/huffman/compress.c" rel="noopener noreferrer"&gt;can be seen here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Decompressing And Reconstructing The Original File
&lt;/h2&gt;

&lt;p&gt;I must say that the decompression is way easier than compression, and it also requires an &lt;a href="https://github.com/buarki/zipper/blob/master/huffman/decompress.h" rel="noopener noreferrer"&gt;API&lt;/a&gt;, similar to the compression. The process kicks off identifying the metada we added during compression (padding bits and tree size) to know where to look for the content of tree and compressed file. You can check the &lt;a href="https://github.com/buarki/zipper/blob/master/huffman/header.c#L43" rel="noopener noreferrer"&gt;implementation here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once we know how many bytes are used to store the tree content we can properly find the beginning of the tree and compressed file. If the bytes of the “shared file” are in the buffer &lt;em&gt;compressedContent&lt;/em&gt;, then the tree content is placed right after the header, thus it can be found at &lt;em&gt;&amp;amp;compressedContent[HEADER_REQUIRED_BYTES]&lt;/em&gt;, while the compressed content is right after the tree, thus &lt;em&gt;&amp;amp;compressedContent[HEADER_REQUIRED_BYTES + treeContentSize]&lt;/em&gt;. It worth pointing that by doing so we are not using extra memory spaces, just pointers pointing to the proper place.&lt;/p&gt;

&lt;p&gt;By knowing where to look for things, we start rebuilding the tree from its content that was attached to the file in preorder. &lt;a href="https://github.com/buarki/zipper/blob/master/huffman/build_huffman_tree_from_compressed_file.c" rel="noopener noreferrer"&gt;The implementation can be seen here&lt;/a&gt; and it worth noticing that it handles the special symbols we have in this project, the “@” and the .&lt;/p&gt;

&lt;p&gt;With the tree rebuilt, we must calculate how many bytes we need for the decompressed file (the original one). This is needed to do because I decided to not include the original file size on the header, because it’d increase the total “shareable” file size. The &lt;a href="https://github.com/buarki/zipper/blob/master/huffman/compute_bytes_required_for_decompressed_file.c" rel="noopener noreferrer"&gt;implementation was a little bit tricky as you can see&lt;/a&gt; but it seems to work :)&lt;/p&gt;

&lt;p&gt;Last, but certainly not least, we effectively fill the buffer of the “decompressed” file, in other words, we rebuild the original file that was compressed. You can check the &lt;a href="https://github.com/buarki/zipper/blob/master/huffman/build_decompressed_file.c" rel="noopener noreferrer"&gt;implementation here&lt;/a&gt;. And we are almost done with the C part.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building The Wasm Binary
&lt;/h2&gt;

&lt;p&gt;In order to create a binary able to run the compression and decompression Emscripten was used. It was done by providing a &lt;a href="https://github.com/buarki/zipper/blob/master/huffman/main.c" rel="noopener noreferrer"&gt;main.c file&lt;/a&gt; exposing the compression and decompression API. And to build it we can use the tool emcc as implemented in the &lt;a href="https://github.com/buarki/zipper/blob/master/Makefile#L3" rel="noopener noreferrer"&gt;project Makefile&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Something very important that must be mentioned is that &lt;strong&gt;Wasm only understand numbers and pointers&lt;/strong&gt;, due to that I broke the compression and decompression in the main.c file in two steps: one to execute the operation and return the number of bytes required by the created compressed or decompressed file, and other one to just collect the compressed or decompressed file content.&lt;/p&gt;

&lt;p&gt;To understand it, let’s check the compression flow from the JavaScript to C. The &lt;a href="https://github.com/buarki/zipper/blob/master/public/app.js#L24" rel="noopener noreferrer"&gt;compress() function (in the app.js file)&lt;/a&gt; is placing the content of the uploaded file into a Uint8Array with length equals to the number of bytes the uploaded file has. It then passes such array alongside its size to the c_compress function via the built in &lt;strong&gt;ccall function&lt;/strong&gt; and getting as output the number of bytes that the compressed file (more precisely, the shareable file, with header, tree, 0s and 1s) requires. Then, it uses this number of bytes to allocate a buffer with this size in the heap. As final step, the created buffer besides the number of bytes required for the compressed size are passed to function &lt;em&gt;receiveCompressedContent&lt;/em&gt;, also via &lt;em&gt;ccall&lt;/em&gt;, where each byte of the compressed file is copied to it. Once the very content gets copied, we do some &lt;strong&gt;pointer math&lt;/strong&gt; to extract it on the JavaScript layer.&lt;/p&gt;

&lt;p&gt;As mentioned, the content of compressed file is copied to a buffer allocated in the Heap. &lt;a href="https://emscripten.org/docs/api_reference/preamble.js.html?highlight=heapu8#id8" rel="noopener noreferrer"&gt;Emscripten allows us to access the heap as a typed array by providing some objects&lt;/a&gt;, as we are dealing with chunks of 8 bits here, we should use &lt;strong&gt;HEAPU8&lt;/strong&gt; (unsigned 8 bits). Such object provides access to the heap through the attribute &lt;strong&gt;buffer&lt;/strong&gt;. Thus, if we have access to the heap, we know where the sequence of bytes of the compressed file is (the address of created buffer) and we know the size of this sequence (how many bytes the compressed file requires) we can extract such bytes creating a “slice” of the Heap from &lt;em&gt;“the buffer address” until the “compressed size bytes”&lt;/em&gt;. If you didn’t get it, keep calm and check bellow example.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6532279lur2axdg8gxmi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6532279lur2axdg8gxmi.png" alt="Heap example" width="720" height="277"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Consider the array &lt;em&gt;unsigned char buffer[] = {‘a’, ‘b’, ‘c’}&lt;/em&gt;. The content of this array is placed at 3 sequential bytes. If you know C a little bit you know that buffer is just a pointer to some address. Assume that for instance such address is 99. Then, we can say that buffer points to 99. As it is a contiguous block of memory, we can access the neighboring byte by doing &lt;em&gt;buffer+ 1&lt;/em&gt;, and we could get (in this example, for sure) 100. And &lt;em&gt;buffer + 2&lt;/em&gt; would gives us 101. Thus, if we collect the content stored at the address 99, 100 and 101 we would properly collect ‘a’, ‘b’ and ‘c’. Actually, in C notation, &lt;em&gt;*buffer&lt;/em&gt;, or &lt;em&gt;*(buffer + 0)&lt;/em&gt;, is equal to ‘a’, &lt;em&gt;*(buffer + 1)&lt;/em&gt; is equal to ‘b’, and &lt;em&gt;*(buffer + 2)&lt;/em&gt; is equal to ‘c’. And in case you didn’t know, yes, &lt;em&gt;buffer[2]&lt;/em&gt; is just a syntactic sugar for &lt;em&gt;*(buffer + 2)&lt;/em&gt; :)&lt;/p&gt;

&lt;p&gt;Once we collected all the bytes of the compressed file to a JavaScript variable/object, we &lt;strong&gt;must deallocate the pointers used to interact with the C binary&lt;/strong&gt; (the one to store the number of bytes needed and the buffer to store the file bytes). This is done by simply applying them to the free() function we exported from C &lt;a href="https://github.com/buarki/zipper/blob/master/Makefile#L6" rel="noopener noreferrer"&gt;during the compilation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With the pointers deallocated, the JavaScript promise is resolved returning an object carrying the size of original file, the size of compressed file and the bytes of the compressed file that will be downloaded to the user’s device. And the same process goes for the decompression.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaways And My Conclusion
&lt;/h2&gt;

&lt;p&gt;First of all, I just loved it all!&lt;/p&gt;

&lt;p&gt;I found the Emscripten tool + documentation nice, actually, I found it more practical to “check and use” than the resources I’ve found for Go.&lt;/p&gt;

&lt;p&gt;Something worth pointing is that sharing content from the C layer to the JavaScript using the Heap might be a bottleneck depending on the size of the object you are passing. In this project, for instance, to handle such “processing times” I added a spinner during compression and decompression. It doesn’t seems to be a dealbreaker though, because depending on the problem we can use different approaches, like processing the hard tasks using service workers.&lt;/p&gt;

&lt;p&gt;You are more than welcome to try it out, check code and suggest some improvements. Hope you enjoyed it :)&lt;/p&gt;

&lt;p&gt;Originally published at &lt;a href="https://www.buarki.com/blog/wasm-huffman" rel="noopener noreferrer"&gt;https://www.buarki.com/blog/wasm-huffman&lt;/a&gt;&lt;/p&gt;

</description>
      <category>c</category>
      <category>javascript</category>
      <category>webassembly</category>
      <category>vercel</category>
    </item>
    <item>
      <title>WebAssembly in 2024: Finding Prime Numbers With Go And Wasm</title>
      <dc:creator>Aurelio Buarque</dc:creator>
      <pubDate>Fri, 16 Feb 2024 06:58:09 +0000</pubDate>
      <link>https://dev.to/buarki/webassembly-in-2024-finding-prime-numbers-with-go-and-wasm-f7d</link>
      <guid>https://dev.to/buarki/webassembly-in-2024-finding-prime-numbers-with-go-and-wasm-f7d</guid>
      <description>&lt;h2&gt;
  
  
  What Is WebAssembly?
&lt;/h2&gt;

&lt;p&gt;It’s been a while since last time I did something with WebAssembly, somewhere back then in 2022. If this word is new to you don’t worry. Putting in simple terms, it is a binary instruction format designed as a portable compilation target for high-level programming languages. It allows code written in languages like &lt;strong&gt;C&lt;/strong&gt;, &lt;strong&gt;C++&lt;/strong&gt;, &lt;strong&gt;Rust&lt;/strong&gt; and &lt;strong&gt;Go&lt;/strong&gt; to be executed in web browsers at near-native speed. WebAssembly is designed to be a low-level, efficient, and secure virtual machine that can be embedded in web pages, providing a platform-independent execution environment for web applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Play With It Now?
&lt;/h2&gt;

&lt;p&gt;From time to time I see people claiming that WebAssembly is close to become a normal tool in our day to day work. With insomnia as my unlikely companion and nothing better to do I decided to check the state of tooling specifically for golang and I intend to show my findings in this one :)&lt;/p&gt;

&lt;h2&gt;
  
  
  A Goal For this Endeavour
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The goal of this project was building a simple app using Go and Wasm to implement the &lt;a href="https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes" rel="noopener noreferrer"&gt;Sieve of Eratosthenes Algorithm&lt;/a&gt;&lt;/strong&gt;. This algorithm is very useful for finding prime numbers up to a given limit. For instance, if you ask for all the prime numbers up to 12 you’ll have 2, 3, 5, 7 and 11.&lt;/p&gt;

&lt;p&gt;The algorithm’s idea is quite simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;we create a list of numbers with the size of the given limit + 1 (just to be able to represent the numbers using indexes);&lt;/li&gt;
&lt;li&gt;we assume that all numbers in such a list are truly prime numbers;&lt;/li&gt;
&lt;li&gt;as we know that 0 and 1 are not prime numbers we mark them as false;&lt;/li&gt;
&lt;li&gt;then, we iterate from 2 up to the square root of the given limit checking if the current number can divide the limit, if so we mark all multiples of the current number as false;&lt;/li&gt;
&lt;li&gt;and then we repeat this process.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I chose this objective because it involves not only transferring data from the “JavaScript part” to the “Go part” but also the reverse process.&lt;/p&gt;

&lt;p&gt;The basic flow is: the user provides a number representing the end of the interval to search for prime numbers. This number is then passed as a parameter to a &lt;a href="https://github.com/buarki/primes-finder/blob/master/cmd/wasm/find_primes.go" rel="noopener noreferrer"&gt;Go function&lt;/a&gt; responsible for identifying prime numbers within the specified range. Subsequently, the discovered prime numbers are returned to the UI layer and displayed as a list.&lt;/p&gt;

&lt;h2&gt;
  
  
  Binding Go and JavaScript
&lt;/h2&gt;

&lt;p&gt;As mentioned earlier, this project has two main parts, let’s call them the Go part and JavaScript part. The Go part is a simple program that makes a function implementing the Sieve of Eratosthenes available to be called by JavaScript when running in a WebAssembly environment. The very app entry point is bellow one:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjpqh8celkuadrnpg1v9f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjpqh8celkuadrnpg1v9f.png" alt="Go entry point" width="553" height="525"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The line &lt;em&gt;js.Global().Set("findPrimes", findPrimes())&lt;/em&gt; serves as the bridge between Go and JavaScript, allowing the function findPrimes be callable from JavaScript. Additionally, the line &lt;em&gt;&amp;lt;-make(chan bool)&lt;/em&gt; just defines a simple channel to prevent the program from exiting immediately. This is typical in WebAssembly programs where the Go runtime does not automatically wait for asynchronous tasks.&lt;/p&gt;

&lt;p&gt;To build the go part we can run:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;GOARCH=wasm GOOS=js go build -o public/main.wasm cmd/wasm/*.go&lt;br&gt;
&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This is basically setting the target architecture for the Go compiler to WebAssembly by using &lt;strong&gt;GOARCH=wasm&lt;/strong&gt; and &lt;strong&gt;GOOS=js&lt;/strong&gt; just targets operating system to JavaScript. The created file main.wasm will be placed at the public directory.&lt;/p&gt;

&lt;p&gt;And another important file is the &lt;strong&gt;wasm_exec.js&lt;/strong&gt;, already provided by Go, thus we just need to copy it. It can be done by running:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" public/wasm_exec.js&lt;br&gt;
&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;With files wasm_exec.js and main.wasm available we can load them on the JavaScript client to call the function &lt;em&gt;&lt;strong&gt;findPrimes&lt;/strong&gt;&lt;/em&gt;. And that’s all for the Go part :)&lt;/p&gt;

&lt;p&gt;You can see the &lt;a href="https://github.com/buarki/primes-finder/blob/master/public/index.html" rel="noopener noreferrer"&gt;full JavaScript part on the project repository&lt;/a&gt;, but the important chunk is bellow one:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1iekex447ge01y2tn5tk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1iekex447ge01y2tn5tk.png" alt="JavaScript wasm code." width="681" height="487"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Besides the input validation, it fetches the WebAssembly binary (main.wasm), instantiates it, and passes the import object from the go instance, this is done by &lt;em&gt;&lt;strong&gt;WebAssembly.instantiateStreaming(fetch(wasmFile), go.importObject).then((result) =&amp;gt; {});&lt;/strong&gt;&lt;/em&gt;. Besides of it, the chunk &lt;em&gt;&lt;strong&gt;go.run(result.instance);&lt;/strong&gt;&lt;/em&gt; executes the Go program inside the WebAssembly instance, making the Go function findPrimes callable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploying To Vercel
&lt;/h2&gt;

&lt;p&gt;Vercel now &lt;a href="https://vercel.com/docs/functions/runtimes/go" rel="noopener noreferrer"&gt;supports Go functions&lt;/a&gt;, and due to the easy integration with Github projects I decided to deploy it there and you can see the &lt;a href="https://primes-finder.vercel.app/" rel="noopener noreferrer"&gt;project running live here&lt;/a&gt;. In case you want to check the code and run it locally you can find the &lt;a href="https://github.com/buarki/primes-finder" rel="noopener noreferrer"&gt;project on my Github&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  My Conclusion So Far
&lt;/h2&gt;

&lt;p&gt;As I could see, indeed there are good learning resources out there, &lt;a href="https://github.com/golang/go/wiki/WebAssembly#getting-started" rel="noopener noreferrer"&gt;like this one&lt;/a&gt;, &lt;strong&gt;but they require you dig deep into it in order to mine useful resources for what you need&lt;/strong&gt;. For sure this is true for any dev tool, but at least, for instance, one can easily find a plenty of tutorials on “&lt;em&gt;how to build a project XYZ using NestJS, Spring Boot, Echo, etc.&lt;/em&gt;”, the same is not true for WebAssembly yet.&lt;/p&gt;

&lt;p&gt;But I really enjoyed it, and I won’t stop with this one. I’ll probably experiment it using C due to the tool I found during this project called &lt;a href="https://emscripten.org/docs/getting_started/Tutorial.html" rel="noopener noreferrer"&gt;Emscripten&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This article was originally posted on my personal site: &lt;a href="https://www.buarki.com/blog/webassembly" rel="noopener noreferrer"&gt;https://www.buarki.com/blog/webassembly&lt;/a&gt;&lt;/p&gt;

</description>
      <category>go</category>
      <category>webassembly</category>
      <category>webdev</category>
    </item>
    <item>
      <title>MLOps in practice: building and deploying a machine learning app</title>
      <dc:creator>Aurelio Buarque</dc:creator>
      <pubDate>Thu, 11 Jan 2024 23:16:49 +0000</pubDate>
      <link>https://dev.to/buarki/mlops-in-practice-building-and-deploying-a-machine-learning-app-2n04</link>
      <guid>https://dev.to/buarki/mlops-in-practice-building-and-deploying-a-machine-learning-app-2n04</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzuxa8y366n2ppncegujj.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzuxa8y366n2ppncegujj.gif" alt="A screen of the app running" width="864" height="864"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Foreword
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;This one aims to provide a friendly introduction to Machine Learning Ops (MLOps) in practice by describing how a simple App able to perform simple mathematical operations using images of single digit numbers was made&lt;/strong&gt;. It has no intentions at all to be a replacement of any deeper study of the topic, but just a simple hands-on example.&lt;/p&gt;

&lt;h2&gt;
  
  
  Context
&lt;/h2&gt;

&lt;p&gt;Talking to some colleagues and friends lately gathering some ideas of a nice Machine Learning project to build, I’ve seen that there’s a gap of knowledge in terms of &lt;strong&gt;how do one exactly uses a Machine Learning model trained?&lt;/strong&gt; Just imagine yourself building a model to solve some problem, you are probably using &lt;a href="https://jupyter.org/" rel="noopener noreferrer"&gt;Jupyter Notebook&lt;/a&gt; to perform some data clean up, perform some normalization and further tests. Then you finally achieve an acceptable accuracy and decides that the model is ready. How will that model end up being used by some API or worker to perform some inference that will be used elsewhere in the company you work or by any system?&lt;/p&gt;

&lt;p&gt;Such question is addressed by the ones involved with &lt;strong&gt;Machine Learning Ops (MLOps)&lt;/strong&gt;, which is a series of practices and policies that takes care of making trained model available to be used somehow, pretty similar to how we export a software lib, a docker image tag etc.&lt;/p&gt;

&lt;p&gt;To provide a concrete, simple, and effective example, this article will go through the very process of planning one app that requires Machine Learning. We’ll pass through the planning and building the needed model, saving the model, checking how it can be exported and how it can be used on an application software.&lt;/p&gt;

&lt;p&gt;All this process was applied on an open project called &lt;strong&gt;snapmath&lt;/strong&gt;, and the very source code is available on &lt;a href="https://github.com/buarki/snapmath" rel="noopener noreferrer"&gt;my Github&lt;/a&gt; and you can also use it as it is deployed &lt;a href="https://snapmath-azure.vercel.app/" rel="noopener noreferrer"&gt;here&lt;/a&gt;. And in case you would like to know the whole design and planning process check the &lt;a href="https://github.com/buarki/snapmath/blob/master/design-doc.md" rel="noopener noreferrer"&gt;project design document&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem to be solved
&lt;/h2&gt;

&lt;p&gt;We want to create a simple calculator of single-digit numbers. Such app will receive the two numbers to be used as &lt;strong&gt;images&lt;/strong&gt; besides the operation to be performed: +, -, / and *. An overview of how the app was planned to look like in the beginning can be seen on the images below (and please don’t envy my prototyping skills):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqyrjf6up0pknj48f34q4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqyrjf6up0pknj48f34q4.png" alt="Snapmath before inputing images" width="800" height="435"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And once user selects the operation to run and input the two images:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6lmhfdm1belmu8cy3dnn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6lmhfdm1belmu8cy3dnn.png" alt="Snapmath after inputing images" width="800" height="434"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How was the problem solved?
&lt;/h2&gt;

&lt;p&gt;With problem stated the main questions that may arise are: how can one properly build the model? Is there a fancy tool available to do so or it should be done on an &lt;em&gt;ad hoc&lt;/em&gt; way? Once the model is trained, how is it exported? as a JSON?&lt;/p&gt;

&lt;p&gt;The tool used to build the model &lt;em&gt;per se&lt;/em&gt; was &lt;a href="https://www.tensorflow.org/" rel="noopener noreferrer"&gt;TensorFlow&lt;/a&gt;, a very powerful and end-to-end open source platform for machine learning with a rich ecosystem of tools. And in order to to create the needed script using TensorFlow &lt;a href="https://jupyter.org/" rel="noopener noreferrer"&gt;Jupyter Notebook&lt;/a&gt; was used, which is a web-based interactive computing platform.&lt;/p&gt;

&lt;p&gt;In machine learning projects, the most important part is &lt;strong&gt;data&lt;/strong&gt;. To build a model to solve such a problem we need a dataset that provides some images of digits with the corresponding digit. A nice and available one is &lt;a href="https://www.kaggle.com/datasets/hojjatk/mnist-dataset" rel="noopener noreferrer"&gt;MNIST&lt;/a&gt;. This dataset provides the images with shape of 28 pixels width x 28 pixels height in grayscale besides its corresponding digit. Such data is provided in form of a CSV file where each line has 729 columns, 728 to represent the pixels (28 x 28) and 1 to represent the expected digit.&lt;/p&gt;

&lt;p&gt;With model built and trained, the next step was loading it into one React app to run the inference of a given image. To load the model we needed to “translate” the trained TensorFlow model on Jupyter Notebook to a form that the &lt;a href="https://www.tensorflow.org/js" rel="noopener noreferrer"&gt;TensorFlow.js&lt;/a&gt; is able to understand. To do so we can use &lt;a href="https://huningxin.github.io/tfjs-converter/" rel="noopener noreferrer"&gt;tfjs-converter&lt;/a&gt;, which is is an open source library to load a pre-trained TensorFlow SavedModel, Frozen Model or Session Bundle into the browser and run inference through TensorFlow.js.&lt;/p&gt;

&lt;p&gt;It sounds like a lot of things to do, but calm down, let’s highlight the goals, non-goals and limitations before proceeding :)&lt;/p&gt;

&lt;h2&gt;
  
  
  Goals of such project
&lt;/h2&gt;

&lt;p&gt;The goals of this project were straightforward: &lt;strong&gt;(1)&lt;/strong&gt; understand the process of building a model from scratch using TensorFlow;&lt;strong&gt;(2)&lt;/strong&gt; get acquainted with Jupyter notebook as it is a widely used tool in by the industry;&lt;strong&gt;(3)&lt;/strong&gt; get acquainted with strategies to deploy Machine Learning models once trained, such as TensorFlow Serving.&lt;/p&gt;

&lt;h2&gt;
  
  
  Non-goals of such project
&lt;/h2&gt;

&lt;p&gt;With this project we are not intended to create production-ready model with high accuracy. The main focus here is having a model with a reasonable accuracy and understand the process from creation to deploy of a Machine Learning model.&lt;/p&gt;

&lt;h2&gt;
  
  
  The limitations of this project
&lt;/h2&gt;

&lt;p&gt;As MNIST dataset was used to train our model, the images to be inserted into the model must be in the shape of 28 pixels width by 28 pixels height and also be in grayscale. It’s reasonable thinking that the majority of people won’t have an image with such traits, thus we’ll need to preprocess it to then input into the model. Due that preprocessing, the image quality might be degraded, leading to bigger possibilities of errors. Thus, it’s recommended to use small images only.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building the model with Convolutional Neural Networks
&lt;/h2&gt;

&lt;p&gt;Due to the fact that numbers can be drawn in different ways, one relevant aspect to plan the Neural Network is the &lt;strong&gt;translation invariance&lt;/strong&gt;, because the app may be fed with a plenty of different forms of number 1s, thus, different forms of invariances, like size, perspective and lighting. Below images might give an example of this.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5yjlaqfouwhoscqvw2ac.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5yjlaqfouwhoscqvw2ac.png" alt="Different images that represent same number" width="742" height="268"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To effectively extract features that enable the identification of a “1” regardless of these variations, we employ convolutional neural networks (CNNs). &lt;strong&gt;The convolutional layers in CNNs are adept at scanning local regions of the input images, enabling the network to learn hierarchical features. This is particularly valuable for capturing essential patterns despite variations in appearance.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And to further make this feature extraction consistent, &lt;strong&gt;we can combine the convolutions with pooling as it helps in creating a more abstract representation of the input, making the model less sensitive to the exact spatial location of features.&lt;/strong&gt; This means that even if the position, size, or lighting conditions of a drawn “1” differ, the CNN’s feature extraction mechanism remains consistent, facilitating accurate identification.&lt;/p&gt;

&lt;p&gt;Thus, the first part of the Neural Network will be in charge of detecting and collecting the features from the images and placing them on a vector. Such vector then will be fed into a regular fully connected Neural Network to perform the learning.&lt;/p&gt;

&lt;p&gt;Once the features that makes a “1” be a “1” are found we can pass it into a fully connected Neural Network and perform the training adjusting the weights until it starts giving accurate predictions of which number the image is corresponding.&lt;/p&gt;

&lt;p&gt;The full process of building such model in baby steps can be seen on &lt;a href="https://github.com/buarki/snapmath/blob/master/machine-learning/snapmath.ipynb" rel="noopener noreferrer"&gt;this Jupyter Notebook file available on my Github&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Exporting the model
&lt;/h2&gt;

&lt;p&gt;Once the model is properly trained, we can export in variety of formats. So far, I’ve only used the format &lt;strong&gt;tf_saved_model&lt;/strong&gt;, and it seems to be the recommended way for now. Once we export it, TensorFlow creates a directory similar to below one:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8d6l79i7ukyktbak1tlo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8d6l79i7ukyktbak1tlo.png" alt="Print of directory with an exported model" width="186" height="152"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It’s also worth pointing that the model should be named using a timestamp for better version control. For above case the name is &lt;strong&gt;1703825980&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Translating the model to TensorFlow.js
&lt;/h2&gt;

&lt;p&gt;As said before, in order to use the built model using TensorFlow.js we should first “translate” it to a compatible format. To do so we used &lt;strong&gt;tfjs-converter&lt;/strong&gt;. By converting the model in format tf_saved_model we get a directory with following structure:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuh33tzyh7qaar6wkhdwe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuh33tzyh7qaar6wkhdwe.png" alt="Print of directory with translated model" width="202" height="75"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Loading the model
&lt;/h2&gt;

&lt;p&gt;TensorFlow.js has a method &lt;strong&gt;loadGraphModel&lt;/strong&gt; which accepts an URL that is serving a model to loaded. As this app is built on top of NextJS, the “translated” model was placed under &lt;strong&gt;public&lt;/strong&gt; directory, and due to that the model importing is done like below image:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkuzpxqpdusjt0q4t0jls.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkuzpxqpdusjt0q4t0jls.png" alt="Exported model being loaded on client side." width="570" height="167"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That way, once the app is rendered on browser the model is loaded and get available to perform an inference on a given image.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preprocessing a given image before running inference
&lt;/h2&gt;

&lt;p&gt;The model was be trained using a 28x28 grayscale image. The majority of images that will be inserted into the model will probably be different than that, for instance, it could be a colorful image, it could have shape 173x100 etc.&lt;/p&gt;

&lt;p&gt;Moreover, the MNIST dataset has a peculiar way to represent images: the “effective image area” representing the number per se has values in the range of 0–255, while the “empty areas” are considered 0. One concrete example of number 3 can be seen below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftpb6vhhlb7cxi6aqj3lu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftpb6vhhlb7cxi6aqj3lu.png" alt="Number 3 image plotted" width="800" height="556"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Real world images are represented using a different approach in which the background is typically lighter than the foreground. &lt;strong&gt;Hence, we need to invert the pixel values.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Due to that, we need to preprocess the given images before running the model inference. This process must involve: (1) converting image in grayscale (to ensure that the input image has a single channel);(2) inverting the pixels (to ensure that foreground is lighter than background like the images we will use to train the model);(3) normalizing the image (apply element wise the division by 255);(4) resizing the image to be 28x28 (the shape that the model will use);&lt;/p&gt;

&lt;p&gt;Such process performed against an image (X, Y, W), where X is the width, Y the height and W the channels, should return an image with shape (28, 28, 1).&lt;/p&gt;

&lt;p&gt;And one last step before feeding the image into the model is creating an input tensor having shape (1, 28, 28, 1), which means a tensor with one element (the image) with 28 pixels of width, 28 pixels of height and 1 channel.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using the app
&lt;/h2&gt;

&lt;p&gt;If you want to just use the app you can visit it &lt;a href="https://snapmath-azure.vercel.app/" rel="noopener noreferrer"&gt;snapmath&lt;/a&gt;. In case you want to run the app locally just clone the &lt;a href="https://github.com/buarki/snapmath/tree/master/app-js" rel="noopener noreferrer"&gt;project&lt;/a&gt; and follow the instructions to run. The repo also provides a version &lt;a href="https://github.com/buarki/snapmath/tree/master/app" rel="noopener noreferrer"&gt;using Python and Flask&lt;/a&gt; and instructions are also available.&lt;/p&gt;

&lt;p&gt;The current version of snapmath looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqnbqlcnmhrk715hfjkac.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqnbqlcnmhrk715hfjkac.png" alt="Overview of the app" width="800" height="406"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This article was originally posted on my personal site: &lt;a href="https://www.buarki.com/blog/mlops-experience" rel="noopener noreferrer"&gt;https://www.buarki.com/blog/mlops-experience&lt;/a&gt;&lt;/p&gt;

</description>
      <category>tensorflow</category>
      <category>mlops</category>
      <category>react</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>Takeaways from 2023, expectations for 2024: linear regression is the new CRUD portfolio project</title>
      <dc:creator>Aurelio Buarque</dc:creator>
      <pubDate>Sat, 23 Dec 2023 20:45:02 +0000</pubDate>
      <link>https://dev.to/buarki/takeaways-from-2023-expectations-for-2024-linear-regression-is-the-new-crud-portfolio-project-5hij</link>
      <guid>https://dev.to/buarki/takeaways-from-2023-expectations-for-2024-linear-regression-is-the-new-crud-portfolio-project-5hij</guid>
      <description>&lt;h2&gt;
  
  
  Foreword
&lt;/h2&gt;

&lt;p&gt;This is a non-technical and non-scientific article. It's simply a mind dump of some key takeaways from my experiences in 2023 and my humble vision for 2024 (something I do every year). Please do NOT make crucial decisions in your life based solely on this :)&lt;/p&gt;

&lt;h2&gt;
  
  
  2023: Highs and Lows
&lt;/h2&gt;

&lt;p&gt;What a year, my friends! The year 2023 will undoubtedly go down in history as a significant milestone, especially with the strides made in the field of Machine Learning that reached the general non-tech public, with GPT-4 taking center stage. Additionally, we cannot overlook the unfortunate wave of massive layoffs that affected many colleagues, some of them close to us. However, my intention here is not to compile software engineering-related news from 2023 but to spotlight key observations I made throughout the year.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Power of Fundamentals: Beyond AI Tools in Software Engineering
&lt;/h2&gt;

&lt;p&gt;I believe it is reasonable to say that many developers are well-equipped with various AI tools, some of which can be integrated into the code editor, enabling AI to effectively engage in pair programming with a developer. But, even having such powerful tools, one thing I could see throughout this year is that such arsenal doesn't mean much if fundamental design principles are not something well understood. I must even say that &lt;strong&gt;in the age of advanced automation and intelligent code suggestions, a robust foundation in design principles is the bedrock of effective software engineering.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This year, I had the chance to work in two important teams at job, and it was interesting to perform code reviews for colleagues using such tools and realizing that while these assistants are powerful aids, they &lt;strong&gt;are not a silver bullet for ensuring code quality&lt;/strong&gt;. In other words, those AI assistants are there to give you whatever you ask, and if you don't have the knowledge or experience to evaluate the given code, then, bad code will be produced the same way, but now for a machine :) How can one evaluate the code given by some AI assistant if it doesn't know why and how to check the cohesion of the code? If it doesn't know why and how to check coupling?&lt;/p&gt;

&lt;p&gt;Over my 10 years in this ever-evolving landscape of software development, I could swap between a series of programming languages, such as C, Java, Go and Javascript with their famous frameworks and libs like Spring Boot, Express, NestJS and so on. I lived enough time to see monoliths be treated as something good, then as the worst thing ever, and in the past few years to get back to be seen as something valuable again. In this ever-shifting terrain, where technologies rise and fall like passing trends, one constant has anchored my proficiency and adaptability: a &lt;strong&gt;commitment to timeless design principles&lt;/strong&gt;. Some principles like SOLID, DRY, YAGNI, "Tell, don't ask" have served as my North Star, providing a reliable compass that transcends the specifics of coding languages.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;As the industry embraces the era of artificial intelligence, I've found good design principles to be more critical than ever, especially in the context of code generated by AI&lt;/strong&gt;. While AI tools can swiftly produce code snippets and even entire functions, their effectiveness is greatly enhanced when paired with a solid foundation in design principles.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two things we know for sure about life: the death and the presence of overengineering
&lt;/h2&gt;

&lt;p&gt;Another point that never gets out of fashion is &lt;strong&gt;overengineering&lt;/strong&gt; things. From my observations, the main cause of it is the desire to apply into a project a set of solutions that big companies had success using... but the project itself is orders of magnitude smaller. An interesting aspect here is that depending on how emotionally attached the solution's owner is, you might be labeled as someone who doesn't follow "good practices". After all, how could someone argue that Domain-Driven Design or Clean Architecture are not suitable for all projects? :)&lt;/p&gt;

&lt;h2&gt;
  
  
  2024: linear regression is the new CRUD portfolio project
&lt;/h2&gt;

&lt;p&gt;Depending on how long you are working in the software industry, you'll probably agree with me that around 10 years ago, a typical portfolio app used to be a &lt;strong&gt;CRUD&lt;/strong&gt; (Create, Read, Update and Delete) using some programming language and some web framework, or it was something like "building a microservice for something". Once I look to the current scenario we are, in which AI and ML are draining attention, and compare with those passed 10 years, I see that &lt;strong&gt;linear regression is the new CRUD&lt;/strong&gt;. I think that because linear regression is a basic step that the majority of developers are able to do, regardless of their preferred stack and it is an effective entry point into the big world of &lt;strong&gt;Artificial Intelligence/Machine Learning&lt;/strong&gt;. Furthermore, I won't be surprise if during next year the amount of Github repos with AI/ML topics increase and if related topics become more frequently featured on CVs.&lt;/p&gt;

&lt;p&gt;Building upon that, something that I also expect for incoming year is &lt;strong&gt;Python&lt;/strong&gt; getting more adoption, not necessarily to be used for web development, or even further as a Javascript replacement, but due to the &lt;strong&gt;high demand for AI&lt;/strong&gt; and the rich ecosystem of libraries available to be used with Python.&lt;/p&gt;

&lt;p&gt;And adding my 2 cents on this: for the ones really interested in stepping into the world of AI/ML, my humble suggestion is to ace the basics of statistics and linear algebra that it requires. Rather than focusing too much on tools, such as Tensorflow, the crucial aspect of working with Machine Learning lies in acquiring the right data, cleaning and normalizing it, and then structuring the model to test hypotheses, check accuracy, and, if satisfactory, export the 'model' to be loaded into some API for execution. &lt;strong&gt;Without a solid understanding of the mathematics behind of it, one won't be able to assess the effectiveness of their model and won't be able to innovate in the field.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Hoping for the best in 2024
&lt;/h2&gt;

&lt;p&gt;I know that recent and rapid advancements and disruptions bring us some uncertainties about the future, which can potentially make us afraid. But, it's precisely these situations that force us to be more resilient and move forward.&lt;/p&gt;

&lt;p&gt;Let's embrace the future with open minds, let's keep our curiosity fueled, and let's remember that amid all the tech jargon and buzzwords, what truly matters are the fundamentals. Whether you're shifting from one programming language to another or from one AI assistant tool to the next, carry with you the timeless principles that lay the foundation for good software.&lt;/p&gt;

&lt;p&gt;As we step into 2024, let's not just hope for a hot tech industry, but actively contribute to turning up the heat. Let's bring the passion, the creativity, and the collaborative spirit that make our industry not just survive but thrive.&lt;/p&gt;

&lt;p&gt;This article was originally posted on my personal site: &lt;a href="https://www.buarki.com/blog/2023-2024" rel="noopener noreferrer"&gt;https://www.buarki.com/blog/2023-2024&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>career</category>
      <category>news</category>
    </item>
    <item>
      <title>Supervised Machine Learning: how to build your own Neural Network from scratch</title>
      <dc:creator>Aurelio Buarque</dc:creator>
      <pubDate>Sun, 03 Dec 2023 21:15:14 +0000</pubDate>
      <link>https://dev.to/buarki/supervised-machine-learning-how-to-build-your-own-neural-network-from-scratch-1ahn</link>
      <guid>https://dev.to/buarki/supervised-machine-learning-how-to-build-your-own-neural-network-from-scratch-1ahn</guid>
      <description>&lt;h2&gt;
  
  
  Foreword
&lt;/h2&gt;

&lt;p&gt;This article aims to provide a simple, minimalist and baby-steps introduction to how &lt;strong&gt;Supervised Machine Learning&lt;/strong&gt; works. It will explain the underlying idea, the mechanics, and the reasons behind its functionality. Additionally, a concrete and minimalist example with code will be available at the end as a reference. Last, but not least, it does not mean to be a replacement of academic articles on this topic, but an introductory material for the ones who love math and coding.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Machine Learning?
&lt;/h2&gt;

&lt;p&gt;In brief, as you can see &lt;a href="https://mitsloan.mit.edu/ideas-made-to-matter/machine-learning-explained" rel="noopener noreferrer"&gt;in this nice article from MIT&lt;/a&gt;, “Machine learning is a subfield of artificial intelligence, which is broadly defined as the capability of a machine to imitate intelligent human behavior. Artificial intelligence systems are used to perform complex tasks in a way that is similar to how humans solve problems”. We can divide this AI field in three categories: &lt;strong&gt;Supervised Learning (and this article will address it), Unsupervised Learning and Reinforcement Learning.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Supervised Learning: an overview
&lt;/h2&gt;

&lt;p&gt;Supervised learning is a branch of machine learning where computers are trained by being presented with examples of desired behavior. It mimics the process of a teacher guiding a student in learning a subject, with continuous checks on the student’s progress.&lt;/p&gt;

&lt;p&gt;For such type of Machine Learning, the algorithm is trained on a dataset that includes both input data and corresponding correct output or “answers.”&lt;/p&gt;

&lt;p&gt;A funny example if this is the App &lt;a href="https://www.youtube.com/watch?v=tWwCK95X6go" rel="noopener noreferrer"&gt;“Hot dog and Not hot dog”&lt;/a&gt; from the Silicon Valley TV Serie in which the character Jian Yang creates an app able to tell whether or not a picture is from a hot dog or not. For sure that’s not the best use case for supervised machine learning, in our day to day life we have much more useful uses like spam filters, product recommendations, fraud detection and so on.&lt;/p&gt;

&lt;p&gt;Some methods used in supervised learning include &lt;strong&gt;neural networks, naïve bayes, linear regression, logistic regression, random forest, and support vector machine (SVM)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;As you can see, even being a subfield of Artificial Intelligence, supervised learning also has its own subtopics. In next section we’ll address how supervised learning works using neural networks.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fundamental pieces of Neural Networks: Neurons and the Universal Approximation Theorem
&lt;/h2&gt;

&lt;p&gt;Dear reader, I must warn you that math is a passion of mine, and I’m thrilled as I write this section. So, proceed at your own risk :)&lt;/p&gt;

&lt;p&gt;As previously discussed, supervised learning works by inputting some data into an algorithm and checking if the outcome matches the expected result. When implementing supervised learning using Neural Networks the output computation will be performed as the input passes through the &lt;a href="https://en.wikipedia.org/wiki/Artificial_neuron" rel="noopener noreferrer"&gt;“neurons”&lt;/a&gt; of the net. A neuron in this case is essentially a function that receives one or more inputs, such as a vector containing values, does some calculation and provides a result. Each input of such neuron will have a weight attached to it to simulate the &lt;a href="https://en.wikipedia.org/wiki/Synapse" rel="noopener noreferrer"&gt;synapse process&lt;/a&gt;. After summing all (inputs * weights) plus a bias value the neuron applies the result into a activation function which is in charge of introducing non-linearity into the network, enabling it to learn and approximate complex relationships in the data. To help you visualize it check bellow image:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkqqtgsmf1vzn5hb5orue.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkqqtgsmf1vzn5hb5orue.png" alt="Image took from https://www.researchgate.net/profile/Douw-Boshoff/publication/328733599/figure/fig2/AS:734165188218886@1552050029499/The-structure-of-the-artificial-neuron.png" width="800" height="463"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In above image each “x” represents an input value fed into the neuron and each “w” is a weight applied to an input. The neuron sums all (x*w) with the bias ( theta letter) and the output of it is applied into the f(x). Mathematicaly we can describe it as the following:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc1j0r8wbwyahwbm0te0y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc1j0r8wbwyahwbm0te0y.png" alt="Neuron equation" width="385" height="152"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Where yk is the output of the kith neuron and f is an activation function, for instance it could be a &lt;a href="https://en.wikipedia.org/wiki/Sigmoid_function" rel="noopener noreferrer"&gt;sigmoid function&lt;/a&gt;. Above equation is enough to explain how a neuron works, but it is &lt;strong&gt;doesn’t explain why it works.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To grasp &lt;strong&gt;why&lt;/strong&gt; it operates, let’s revisit the fundamental concept of supervised learning: we possess a labeled dataset that serves as the training data for a Neural Network, allowing it to comprehend the underlying patterns. Consider the following example: we aim to create a function, denoted as f(x), which, based on the hours of study by a computer science student, predicts their linear algebra test score. We assume that the student has already taken three tests, and we have their corresponding scores:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpzqt8082t1fu31m518jd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpzqt8082t1fu31m518jd.png" alt="Table where left column shows the hours of study and right column the test score." width="216" height="161"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Generalizing the idea we get:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvbqczrltbvvhiljv2fi7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvbqczrltbvvhiljv2fi7.png" alt="Table generalizing the idea of the presented problem." width="271" height="244"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thus, our job here is to find a function f(x) so that: f(xi) is very close to yi, where i = 0,1,2…n. To do so we define a family of functions F that could be a good fit for this problem, like &lt;strong&gt;linear regression&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd6abpy8jcrxduyqdceuw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd6abpy8jcrxduyqdceuw.png" alt="Family of functions we need." width="625" height="57"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If, and only if, there really exists a function that could represent the relation between xi and yi we can take this as an &lt;a href="https://umanitoba.ca/science/research/mathematics/approximation-theory#:~:text=Learn%20more-,Approximation%20theory,-Approximation%20theory%20studies" rel="noopener noreferrer"&gt;approximation problem&lt;/a&gt;: how can we find an element from F as close as possible from our desired function?&lt;/p&gt;

&lt;p&gt;We don’t all have corresponding yi for all xi, only a few samples where for sure f(xi) = yi. Thus, the best we can do is find a function that properly fits the relation between xi and yi, generalize unknown examples and is computationally possible to find. In other words, we want an element from F that minimizes the &lt;a href="https://en.wikipedia.org/wiki/Uniform_norm" rel="noopener noreferrer"&gt;supremum norm&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9ik699tvcwvcs072oemw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9ik699tvcwvcs072oemw.png" alt="The best approximation." width="480" height="92"&gt;&lt;/a&gt;&lt;br&gt;
The best approximation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3qi6urpa9snrow8u52yg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3qi6urpa9snrow8u52yg.png" alt="The supremum norm." width="622" height="68"&gt;&lt;/a&gt;&lt;br&gt;
The supremum norm.&lt;/p&gt;

&lt;p&gt;As we want the best fit between input data and output as possible we need to find the minimal || f — u||. The supremum norm is a mathematical tool that helps us measure the maximum vertical distance between the true function and our approximation across all possible x values. &lt;strong&gt;By minimizing this supremum norm, we seek a function within F that provides the closest possible approximation to our desired function&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A proof that the mathematical Neuron definition I’ve shown above is capable of finding the minimal || f — u|| is given by the &lt;a href="https://www.deep-mind.org/2023/03/26/the-universal-approximation-theorem/#:~:text=provide%20an%20answer.-,Universal%20Approximation%20Theorem" rel="noopener noreferrer"&gt;Universal Approximation Theory (UAT)&lt;/a&gt;, and this &lt;a href="https://www.youtube.com/watch?v=O45AaRPQhuI" rel="noopener noreferrer"&gt;quick video&lt;/a&gt; shows why Neural Networks are able to learn pretty much any function due to the UAT.&lt;/p&gt;

&lt;p&gt;The overall process is defining a small ε value that will control how closely the elements of F will fit our model. &lt;strong&gt;The smaller ε is more accurate our Neural Network will be&lt;/strong&gt;. Once ε is defined we start searching a Neural Network in which the outputs are close to our desired f, bellow image (tries) to show it:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7ancv0azpi4h833w5fh5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7ancv0azpi4h833w5fh5.png" alt="Neural Network trying to fit the model." width="720" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Neural Network trying to fit the model.&lt;/p&gt;

&lt;p&gt;Although the UAT proves that such function u(x) (an element of F) exists &lt;strong&gt;it doesn’t tell us how to find it&lt;/strong&gt; and the &lt;strong&gt;required amount of neurons needed to fit &lt;em&gt;f(x)&lt;/em&gt; might be really big&lt;/strong&gt;, due to that finding an optimal Neural Netwok in practice is a challenge. Besides of it there are other points to take care while chasing for a Neural Network, such as avoiding &lt;strong&gt;overfitting&lt;/strong&gt; and &lt;strong&gt;underfitting&lt;/strong&gt;, the &lt;strong&gt;quantity&lt;/strong&gt; and &lt;strong&gt;quality&lt;/strong&gt; of data required, computational resources available and so on.&lt;/p&gt;

&lt;p&gt;Such challenges will be addressed in next section, by assessing a simple problem as example and showing one possible way to find an optimal approximation (a Neural Network that fits the behavior of an f).&lt;/p&gt;

&lt;h2&gt;
  
  
  Defining a concrete problem as example to solve and planning data architecture
&lt;/h2&gt;

&lt;p&gt;We want to create a model that based on the amount of hours of study and the amount of hours of meditation of a computer science student we find its linear algebra test score. We can also assume that the student has already attempted to perform three tests and we have their respective score:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgar4zbgu0msulda35jbg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgar4zbgu0msulda35jbg.png" alt="Table showing a simple dataset of the problem we are trying to solve in this article. First two columns are, respectively, the hours of sleep and hours of meditation. The third one represents the test score." width="399" height="159"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Table showing a simple dataset of the problem we are trying to solve in this article. First two columns are, respectively, the hours of sleep and hours of meditation. The third one represents the test score.&lt;/p&gt;

&lt;p&gt;If we assume that the model we are trying to create is a function, then the &lt;em&gt;hours of sleep&lt;/em&gt; and &lt;em&gt;hours of meditation&lt;/em&gt; are inputs of such function, while &lt;em&gt;score&lt;/em&gt; is the output. Let’s define the vector X as the input of our model and Y as the output of it. Just to clarify it, if &lt;em&gt;f(x)&lt;/em&gt; represents our Neural Net and one possible input is &lt;em&gt;X=[6,5]&lt;/em&gt; (where 6 is the hours sleept and 5 hours of medidation) then &lt;em&gt;f(X)=[8]&lt;/em&gt; (where 8 is the test score). The same way would be if &lt;em&gt;X=[7,4]&lt;/em&gt; and &lt;em&gt;f(X)=[7]&lt;/em&gt; and also if &lt;em&gt;X=[7,5]&lt;/em&gt; and &lt;em&gt;f(X) = [8]&lt;/em&gt;. A clever way to approach how we group inputs and outputs would be using matrices of them, then we would have the input matrix:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fss6eho9o57x7mwnx4qhu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fss6eho9o57x7mwnx4qhu.png" alt="Matrix with the Neural Network inputs." width="410" height="349"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Matrix with the Neural Network inputs.&lt;/p&gt;

&lt;p&gt;Each line on above matrix X is a valid input where the left column represents the hours of sleep and the right column the hours of meditation. And then &lt;em&gt;f(X)&lt;/em&gt; will be:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmvlfworo42wggntxmv1g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmvlfworo42wggntxmv1g.png" alt="Matrix with the Neural Network outputs.&amp;lt;br&amp;gt;
" width="403" height="346"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Matrix with the Neural Network outputs.&lt;/p&gt;

&lt;p&gt;Each line on above matrix Y is a valid output of the Neural Network.&lt;/p&gt;

&lt;p&gt;One thing that must be pointed is that &lt;strong&gt;both matrices data must be scaled to ensure that the Neural Network handles standardized units&lt;/strong&gt;. One way to achieve it is using &lt;a href="https://www.oreilly.com/library/view/hands-on-machine-learning/9781788393485/fd5b8a44-e9d3-4c19-bebb-c2fa5a5ebfee.xhtml#:~:text=Min%E2%80%93max%20normalization" rel="noopener noreferrer"&gt;Min-max normalization&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With the problem stated and input features known we can plan how the Neural Network structure will be. As our &lt;strong&gt;input data has two dimensions&lt;/strong&gt; we need an input layer with two inputs, and as &lt;strong&gt;our result is a single number&lt;/strong&gt; our network has one output only. Thus, the basic structure required for our Neural Network is the following:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6n39ekil9yh2xdzui28k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6n39ekil9yh2xdzui28k.png" alt="The basic structure of our Neural Network." width="720" height="340"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The basic structure of our Neural Network.&lt;/p&gt;

&lt;p&gt;To complete the structure we need to plan the &lt;strong&gt;hidden layers&lt;/strong&gt; of it. The amount of inner layers may vary depending on the problem the Neural Network needs to solve. In fact, real world examples could have millions of hidden layers. For the sake of simplicity we’ll stick if one hidden layers with 3 neurons only, and bellow image image shows it:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwacv2fkhw04xfhc3iqti.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwacv2fkhw04xfhc3iqti.png" alt="The completed structure of our Neural Network." width="720" height="494"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Some important things missing on image above are the &lt;strong&gt;neuron weights&lt;/strong&gt;. We will assume that the weights of first layer are always 1 and ignore them, so applying them on the second and third layer we get:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb2xyj25wusrjn0rv4e5v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb2xyj25wusrjn0rv4e5v.png" alt="The completed structure of our Neural Network with Neuron weights." width="720" height="494"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The completed structure of our Neural Network with Neuron weights.&lt;/p&gt;

&lt;p&gt;In case you didn’t get the weight notation take a look at bellow image:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzvmcduzyzws8geeiboam.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzvmcduzyzws8geeiboam.png" alt="The completed structure of our Neural Network with Neuron weights." width="569" height="483"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In next section we will properly address how the Neural Network model is built based on this proposed structure.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Neural Network model in action: forward process
&lt;/h2&gt;

&lt;p&gt;The process of acctually using the Neural Network is called forward process. Here the Neural Network receive the inputs, pass all of them through the weights, apply the sum into the activation function and proceed to next layer.&lt;/p&gt;

&lt;p&gt;Let’s start checking how the process works on the second layer. Each neuron inside of second layer will receive the input from all neurons from first layer and apply a weight on it. Once all inputs are weighted we will get the following:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvl83bj340u7yhiib8u6l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvl83bj340u7yhiib8u6l.png" alt="Neural Network with all second layer’s input weighted.&amp;lt;br&amp;gt;
" width="720" height="535"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Neural Network with all second layer’s input weighted.&lt;/p&gt;

&lt;p&gt;Considering that we placed all input values into the matrix X and it is a 3x2 matrix, if we place all weights also on a matrix 2x3 we will end up having a matrix containing the results of all (inputs * weights). Let the weight matrix of second layer be:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F87pd11fxaphu94x1w6yq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F87pd11fxaphu94x1w6yq.png" alt="Matrix with second layer weights." width="720" height="489"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then, the product:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1zuy1hxeg53js099huy9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1zuy1hxeg53js099huy9.png" alt="Matrix product." width="192" height="143"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Will be:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fit75muye53r8ubjqbvrj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fit75muye53r8ubjqbvrj.png" alt="Matrix product." width="720" height="344"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We’ll call the above product as:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftv8vt4y2jfrd898cy9zf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftv8vt4y2jfrd898cy9zf.png" alt="Matrix product." width="272" height="86"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Even before you ask: we’ll address the bias in next sections.&lt;/p&gt;

&lt;p&gt;Once we calculated the weighted sum for each neuron we can now apply the activation function element wise on above matrix to calculate the output of second layer. For this problem, the activation function used will be the sigmoid:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpeuhjs94cuvu021e5evz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpeuhjs94cuvu021e5evz.png" alt="Sigmoid activation function." width="267" height="94"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sigmoid activation function.&lt;/p&gt;

&lt;p&gt;Thus, the second layer output can be defined as:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftxsqnfvvm8l0cgtsxbue.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftxsqnfvvm8l0cgtsxbue.png" alt="Matrix containing second layer output.&amp;lt;br&amp;gt;
" width="269" height="106"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Matrix containing second layer output.&lt;/p&gt;

&lt;p&gt;Once second layer is finished we proceed to the third layer with exact same process and with the second layer output being the third layer input. Let’s start by seeing the third layer weights:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy7z1vh2r2i1t1l71002w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy7z1vh2r2i1t1l71002w.png" alt="Matrix carrying the weights of third layer." width="224" height="302"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Matrix carrying the weights of third layer.&lt;/p&gt;

&lt;p&gt;Multiplying the second layer output with third layer weights will gives us:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft7gy89pib1dzks0grbcb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft7gy89pib1dzks0grbcb.png" alt="Matrix values" width="720" height="269"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And to get the output of third layer we apply the sigmoid on this product:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbnqehd2zll2v0z5wr1qs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbnqehd2zll2v0z5wr1qs.png" alt="Matrix containing third layer output." width="256" height="98"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Above matrix has shape 3x1 and each line represents the predicted test score.&lt;/p&gt;

&lt;p&gt;With the equations discussed in this section we are able to predict the test score based on the amount of study and medidation hours. But such predictions will probably be absolutely wrong :). &lt;strong&gt;This is due to the fact that the weights from our layers once we create the Neural Network are defined randomly&lt;/strong&gt;. Thus, the Neural Network won’t be useful on helping us predicting the test score. To adjust the Neural Network we need a way to find proper values for our weights while we measure the accuracy of outputs. This will be addressed in next section.&lt;/p&gt;

&lt;h2&gt;
  
  
  Assessing the Neural Network error: error cost
&lt;/h2&gt;

&lt;p&gt;In order to improve the Neural Network accuracy we need to know how wrong the outputs are. To do so we’ll use a &lt;strong&gt;cost function&lt;/strong&gt; (or loss function). &lt;strong&gt;The cost function will measure the performance of our machine learning model quantifying the error between the prediction and expected value&lt;/strong&gt;. A good way to address the cost function is using the &lt;a href="https://www.statisticshowto.com/probability-and-statistics/statistics-definitions/mean-squared-error/" rel="noopener noreferrer"&gt;Mean Squared Error (MSE)&lt;/a&gt; (soon it will be explained why such approach is effective and smart). In mathematical terms we can define MSE by the following:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkix4v4v1inaof4onhqal.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkix4v4v1inaof4onhqal.png" alt="The error of our Neural Net" width="417" height="134"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The error of our Neural Net&lt;/p&gt;

&lt;p&gt;On above equation, E is the global error of our Neural Network,&lt;em&gt;y'&lt;/em&gt; is the expected output for one input and y is the received output for one input. In the case you are wondering why we placed the term 1/2: it was done for convenience during derivatives operations as it simplifies the computation of gradients and it doesn’t alter the fundamental meaning of Mean Squared Error (MSE), which remains a measure of the average squared difference between predicted and actual values.&lt;/p&gt;

&lt;p&gt;Being brief, &lt;strong&gt;training a Neural Network means minimize its cost function&lt;/strong&gt;. Our cost function is a function of the Neural Network input and its weights, and in case this is not clear we can expand E:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyrmzjv2wc1xvn8l3219o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyrmzjv2wc1xvn8l3219o.png" alt="Expanding the Neural Network error cost." width="553" height="676"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We just don’t have control over the X param, only the “client” of the Neural Network does. &lt;strong&gt;Thus, if there is something we can change in order to adjust the cost function is the Neural Network weights&lt;/strong&gt;. But how can we adjust the weights? Brute force?&lt;/p&gt;

&lt;p&gt;If we consider for a while a hypothetical Neural Network containing one weight only in which the “proper” value for it can be found on a set of 10k numbers, a brute force approach would take in the worst case 10k “checks” to find the proper values. If we also assume that each “check” takes 2 milliseconds (ms), then, the overall process would take 20 seconds. Anoying but still feasible.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frqgg1nh5apjugmateie4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frqgg1nh5apjugmateie4.png" alt="How the error cost would look like for one neuron." width="720" height="724"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It would takes us 10k times executing the forward process, calculating the error cost to, in the end, find the minimal error value. If we expand it to two neurons and we assume that this new neuron also has the proper value on a search space of 10k numbers, in such scenario, our search would require 100000000 iterations (10k * 10k), and if we also keep the check time of 2ms it would take 200,000 seconds or approximately 56 hours :)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk35myruryhf04dkvl0xn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk35myruryhf04dkvl0xn.png" alt="How the error cost would look like for two neurons.&amp;lt;br&amp;gt;
" width="720" height="663"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The conclusion here is obvious: &lt;strong&gt;the more neurons the Neural Network has, more expensive its training will be&lt;/strong&gt;. This phenomenon is called &lt;a href="https://en.wikipedia.org/wiki/Curse_of_dimensionality" rel="noopener noreferrer"&gt;Curse of dimensionality&lt;/a&gt;, which is a serie of challenges and issues that arise when dealing with data in high-dimensional spaces.&lt;/p&gt;

&lt;p&gt;As we could see, &lt;strong&gt;bruce force solutions are not feasible here&lt;/strong&gt;. We need a way to find a shortcut between all the available numbers in the search space to the ones that gives us the minumum of error cost function. &lt;strong&gt;We could achieve it by kwnowing the direction that E decreases from one inspected point&lt;/strong&gt;. Let’s consider the scenarion with one single neuron again to visualize it. Consider bellow image where point P(wi, E(wi)) is a known point in the availble search space and we know that moving to the right decreases E while moving to the left increases E. Then, as we want to minimize E we can move to the right.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxf6wo06n8ka7v2rpk94v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxf6wo06n8ka7v2rpk94v.png" alt="Error and weight" width="720" height="678"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can extend this ideia by asking: how E behaves when the weights changes? And fortunely there is an awesome tool to helps us finding it out called calculus, specifically the the derivatives. As mentioned above, E is a function of both Neural Network inputs and the weights, but we control only the weights. &lt;strong&gt;Thus, to know how E behaves by modifying weights we need to calculate the derivative of E with respect of the weights&lt;/strong&gt;. If derivative of E with respect to the weights is positive we can say that E is increasing, otherwise it is decreasing. For the case of our Neural Network, which has more than one layer, we need to perform a parcial derivative of E with respect to each layer weight.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;By knowing the direction that E decreases we can iteratively look for lower values of E, ignoring points where it increases optmizing the search time.&lt;/strong&gt; Check bellow image to visualize it:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbelqdol93pysi3kbz11o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbelqdol93pysi3kbz11o.png" alt="A clever way to find lower values of E." width="720" height="684"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The method described above is called &lt;a href="https://towardsdatascience.com/gradient-descent-algorithm-a-deep-dive-cf04e8115f21#:~:text=Gradient%20descent%20(GD)%20is%20an,e.g.%20in%20a%20linear%20regression" rel="noopener noreferrer"&gt;Gradient Descent&lt;/a&gt;.), which is a way to minimize the cost function by adjusting the model params in such a way that the cost decreases faster. &lt;strong&gt;It works by calculating the gradient (slope) of the cost function with respect to the parameters and updating them in the opposite direction of the gradient, moving towards the minimum of the cost function&lt;/strong&gt;. We basically repeat this process until the cost function reaches the minimal value.&lt;/p&gt;

&lt;p&gt;An important thing to point is that &lt;strong&gt;we can use Gradient Descent for our Neural Network because our error cost function has a convex shape&lt;/strong&gt;, which ensures that E always moves on “the same direction”. The convex shape is a consequence of using the MSE, which is a quadratic function :)&lt;/p&gt;

&lt;p&gt;If our cost function didn’t have such convex shape it could mess our Gradient Descent execution, and our search could end up being stuck on a &lt;strong&gt;local minimum&lt;/strong&gt; rather than the &lt;strong&gt;global minimum&lt;/strong&gt;. Check bellow image:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgq8291b60fub0rwr01ix.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgq8291b60fub0rwr01ix.png" alt="Showing the problem of local and global minimum." width="720" height="572"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It also worth poiting that for some scenarios the shape of cost function doesn’t matter, for instance in the case of using &lt;a href="https://www.analyticsvidhya.com/blog/2020/10/how-does-the-gradient-descent-algorithm-work-in-machine-learning/#:~:text=for%20large%20datasets.-,Stochastic%20Gradient%20Descent,-Stochastic%20gradient%20descent" rel="noopener noreferrer"&gt;Stochastic Gradient Descent&lt;/a&gt;, where you use one input example at time. In our case we’ll be using &lt;a href="https://www.analyticsvidhya.com/blog/2020/10/how-does-the-gradient-descent-algorithm-work-in-machine-learning/#:~:text=used%20in%20practice.-,Batch%20Gradient%20Descent,-Batch%20gradient%20descent" rel="noopener noreferrer"&gt;Batch Gradient Descent&lt;/a&gt;, where rather than one training example at time we’ll use a set of it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Backpropagation: adjusting weights using Gradient Descent
&lt;/h2&gt;

&lt;p&gt;To perform the Batch Gradient Descent process to update the Neural Network weights we need to calculate the partial derivative of E with respect to our weights from second and third layer. Let’s start with third layer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffc2z3ywaynowusff5b53.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffc2z3ywaynowusff5b53.png" alt="dEdW3" width="591" height="151"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As the &lt;a href="https://www.youtube.com/watch?v=fIOMrpVLtBs" rel="noopener noreferrer"&gt;derivative of a sum is the sum of derivatives&lt;/a&gt; we can move the inner sum to the outside:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0m7lv6lj6mulbehcub3d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0m7lv6lj6mulbehcub3d.png" alt="Applying derivative rule" width="576" height="125"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For simplicity, let’s remove the outter sum and focus on the derivative part of an isolated case. That said, if we apply the &lt;a href="https://www.youtube.com/watch?v=H-ybCx8gt-8" rel="noopener noreferrer"&gt;chain rule&lt;/a&gt; we would get:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F279lo8vj2wxklvb5pjsf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F279lo8vj2wxklvb5pjsf.png" alt="Chain rule" width="636" height="114"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As the term &lt;em&gt;y’&lt;/em&gt; is the expected output then it is a constant. Resolving the equation we get:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9hw9mfhcorngn3yowdv0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9hw9mfhcorngn3yowdv0.png" alt="Solving equation" width="421" height="105"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If we remember that y (the output of Neural Network) is the result of the activation function applied on &lt;em&gt;v(3)&lt;/em&gt; and apply the chain rule again we get:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F982x8mzeqqhrq4w0yhbz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F982x8mzeqqhrq4w0yhbz.png" alt="Solving equation" width="490" height="122"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The derivative of second term is the derivative of our activation function S, which is a sigmoid. The derivative of S is:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Finlimfxwapswk967pm4a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Finlimfxwapswk967pm4a.png" alt="Sigmoid prime" width="236" height="103"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Applying it we get:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu5888myp4sdr2ypzcc8r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu5888myp4sdr2ypzcc8r.png" alt="Solving equation" width="511" height="117"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This part is a little bit tricky due to matrix dimensions, but don’t give up :)&lt;/p&gt;

&lt;p&gt;A few iterations ago we have removed the Σ from our calculations to look to the problem isolatedly and now we can go back to it. Both matrices y’ and y have same dimentionality and the subctration between them can occur without any problem. The interesting part is what to do with S’, as it has the same dimension of the matrix subtraction. If we look closely on how this product would happen if we were applying the Σ summation we could see that this multiplication would be done element-wise. Thus, to represent it in the form of matrices mutiplication we can use the &lt;a href="https://en.wikipedia.org/wiki/Hadamard_product_(matrices)" rel="noopener noreferrer"&gt;Hadamard Product&lt;/a&gt;. Adding the proper notation to it we get:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj7nae9x98hdlgqv8vp99.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj7nae9x98hdlgqv8vp99.png" alt="Applying notation" width="529" height="117"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The last derivative to solve is the third term, which result is the Y(2). As in this step we are &lt;strong&gt;propagating the error backwards to the weight that generated it&lt;/strong&gt; we need to mutiply the elements of Y(2) to its proper &lt;strong&gt;back propagating error&lt;/strong&gt;. We can do so by multiplying the transpose of Y(2) by the first two terms:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyop8wwa0a7qkcd405qca.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyop8wwa0a7qkcd405qca.png" alt="Solved equation" width="520" height="102"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And we are done with third layer! Proceeding to the second layer, the overall process is the same, the difference is that now we’ll perform the derivative of E with respect to W(2). We can start with:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6t2t5q7f20hhk7y3en7n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6t2t5q7f20hhk7y3en7n.png" alt="Solving equation" width="340" height="102"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Solving it we’ll have:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fifcmco4r2dhnecf4t4vp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fifcmco4r2dhnecf4t4vp.png" alt="Solving equation" width="522" height="375"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Applying the chain rule we get:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff4n12os8uba8gsv1wmit.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff4n12os8uba8gsv1wmit.png" alt="Solving equation" width="400" height="96"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The first two terms are already known:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feq6nla50cvy9w37mzqtp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feq6nla50cvy9w37mzqtp.png" alt="Solving equation" width="437" height="84"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And if we keep applying the chain rule:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpupmxit9koj3rtob6tit.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpupmxit9koj3rtob6tit.png" alt="Solving equation" width="600" height="488"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If we reorganize some terms on above equation and also on the one for the third layer we’ll see they have a product in common:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo97gmmjhtayj617vpffx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo97gmmjhtayj617vpffx.png" alt="Solved equations" width="584" height="208"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This product is the back propagating error of third layer. We can add the following notation to it:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpsizied2jf8dlqrhh2iw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpsizied2jf8dlqrhh2iw.png" alt="Solved equation" width="376" height="97"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can rewrite the equation for second layer to be the following:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6sz8e1buzc0tcheurorn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6sz8e1buzc0tcheurorn.png" alt="Solved equation" width="425" height="129"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If we extend the idea, we can also define the back propagating error to the second layer and we’ll have the following:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fagtj066ir99ttikyia3n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fagtj066ir99ttikyia3n.png" alt="Solved equation" width="388" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once we finished the equations for both second and third layer we are able to adjust the Neural Network weights by subtracting them with each respective gradient component. &lt;strong&gt;We need to subtract because the gradient points in the direction of the steepest increase of the error, to reduce the error we need to move in the opposite direction of the gradient&lt;/strong&gt;. Thus, by subtracting we are adjusting the weights in the direction that decreases the error.&lt;/p&gt;

&lt;p&gt;We can also mutiply our gradient component for a small scalar to control the size of the steps taken during each iteration of the optimization process. &lt;strong&gt;This scalar is useful because it controls the size of the weight adjustments, ensuring that the optimization process is stable and converges to a meaningful solution&lt;/strong&gt;. This scalar is often called &lt;strong&gt;learning rate&lt;/strong&gt;. Finding a proper learning rate is important because &lt;strong&gt;a too-large learning rate can cause the optimization process to oscillate or diverge, potentially missing the minimum&lt;/strong&gt;. On the other hand, &lt;strong&gt;a too-small learning rate can make the convergence very slow, requiring many iterations to reach the minimum&lt;/strong&gt;. By doing some researches we can find some suggestions on how to configure out the learning rate, one common suggestion is &lt;a href="https://www.andreaperlato.com/theorypost/the-learning-rate/#:~:text=The%20range%20of%20values%20to%20consider%20for%20the%20learning%20rate%20is%20less%20than%201.0%20and%20greater%20than%2010%5E%2D6.%20A%20traditional%20default%20value%20for%20the%20learning%20rate%20is%200.1%20or%200.01%2C%20and%20this%20may%20represent%20a%20good%20starting%20point%20on%20your%20problem" rel="noopener noreferrer"&gt;something between 0.01 and 1.0&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thus, to update weights matrices we do the following:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgp8u079hxgqv25zguhf0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgp8u079hxgqv25zguhf0.png" alt="Solved equations" width="352" height="174"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Where α is the learning rate. One thing worth point specifically during the implementation of such step is to &lt;strong&gt;test the implementation to ensure it was well done&lt;/strong&gt;. Any implementation glitch on this step would lead to wrong Neural Network results and problems very hard to debug :)&lt;/p&gt;

&lt;h2&gt;
  
  
  Training the Neural Network
&lt;/h2&gt;

&lt;p&gt;Once we have designed the Neural Network structure, planned its forward process and the backpropagation process we are able to effectively train it. The idea is pretty simple: we define a param to controll how many “iterations” the training will have, usually this param is called &lt;a href="https://www.simplilearn.com/tutorials/machine-learning-tutorial/what-is-epoch-in-machine-learning#:~:text=EXPLORE%20PROGRAM-,What%20Is%20Epoch%3F%C2%A0,-An%20epoch%20is" rel="noopener noreferrer"&gt;epoch&lt;/a&gt;, and also the already mentioned &lt;strong&gt;learning rate&lt;/strong&gt;. The learning rate, the amount of epochs and the number of layers are examples of what we call &lt;a href="https://towardsdatascience.com/what-are-hyperparameters-and-how-to-tune-the-hyperparameters-in-a-deep-neural-network-d0604917584a" rel="noopener noreferrer"&gt;hyperparameters&lt;/a&gt;. Once we define such numbers the process goes like: &lt;strong&gt;for each epoch, execute the forward process, compute the error between the expected values and received values, calculate the gradient descent and update the weights&lt;/strong&gt;. A very high level algorithm would be:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhtol84qpew6ah12p4p8l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhtol84qpew6ah12p4p8l.png" alt="A simple way to train" width="720" height="124"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With a reasonable amount of data with proper quality, time and computer power, above process will do the job, but there are some pitfalls during such process, specially for Neural Networks with a much bigger number of layers. For instance, finding a proper learning rate is a non trivial task and a bad weights initialization can result slow convergence or even convergence to suboptimal solutions. There are alternatives that can be explored such as &lt;a href="https://medium.com/codechef-vit/why-bfgs-over-gradient-descent-3ecc3e7ffd" rel="noopener noreferrer"&gt;BFGS technique&lt;/a&gt;, as it can perform a faster convergence and does not requires a manual definition of the learning rate.&lt;/p&gt;

&lt;p&gt;Now we are missing to address two points to complete our basic knowledge about Neural Networks: bias and regularization. We’ll discuss them in next sections.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bias: enhancing flexibility
&lt;/h2&gt;

&lt;p&gt;One component that so far we skipped is the bias on Neural Networks. An important job it does is giving to the Neural Network the hability to learn different constants for each neuron, &lt;strong&gt;which is crucial for fitting diverse patterns in the data&lt;/strong&gt;. It also helps Neural Networks to model nonlinear relationships. Moreover, it allows neurons to be “activated” even when the weighted sum of inputs is below zero, and this is important for modeling situations where certain features may not contribute much to the output individually but still play a role when combined with other features.&lt;/p&gt;

&lt;p&gt;In order to introduce the use of bias in our model we might need to adjust the found equations a little bit. The first adjustment is on how we calculate the weighted input, now we need to add a matrix B containing the biases:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsf3gz8cnszyiqzozdpn6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsf3gz8cnszyiqzozdpn6.png" alt="Adding Bias" width="337" height="156"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And we also need to update &lt;em&gt;B(2)&lt;/em&gt; and &lt;em&gt;B(3)&lt;/em&gt; during the backpropagation. The ideia is the same applied to weights, so if we start with &lt;em&gt;B(3)&lt;/em&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqi85jx0heb950u7szft0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqi85jx0heb950u7szft0.png" alt="Solving equations" width="513" height="772"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And to update B(3) we do:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk9sn5m2i2i1gdh3n4pnc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk9sn5m2i2i1gdh3n4pnc.png" alt="Solved bias" width="271" height="47"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For B(2) we also do the same process, and due to that we can ommit some parts:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3fir86l9s9bh998ewkvn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3fir86l9s9bh998ewkvn.png" alt="Solved bias" width="434" height="833"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And to update it:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9ih0rf6syb32c31dbz7l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9ih0rf6syb32c31dbz7l.png" alt="Solved bias b2" width="277" height="71"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we can finally move to last step :)&lt;/p&gt;

&lt;h2&gt;
  
  
  Overfitting: we can’t ignore the noise!
&lt;/h2&gt;

&lt;p&gt;Let’s assume that in a few days the same student will have another linear algebra test, and it is hopping to pass it now :). So the student uses our Neural Network to find a proper combination of hours of study and meditation to succeed on the test. It concludes that a score 7 is enough and testing based on the vector X=[7,4], which also is record from its previous test, it decides to study for 7 hours and medidate for 4. Once the test score is informed it sees it scored 5 :(&lt;/p&gt;

&lt;p&gt;There can be several reasons for the model predict such wrong score, like the student missing to review a topic in which the test focused more, the student having headache during the test, or even back luck.&lt;/p&gt;

&lt;p&gt;The important point here is the &lt;strong&gt;data we use to design our model is just a perception of a fact, but between the fact &lt;em&gt;per se&lt;/em&gt; and our observation there is noise. In other words, there are other factors that influence the test score, and some of them we might not even be aware that they exist&lt;/strong&gt;. If a model doesn’t take it into account the Neural Network can endup having overfitting issues, which is when the Neural Network performs good with training data but badly with unseen data.&lt;/p&gt;

&lt;p&gt;Overfitting verification is a must have during the Neural Network training and one interesting way to do so is split the training data in two parts: a bigger one to train the Neural Network while iterating through some epochs, and a smaller one to evaluate if the training caused an overfitting.&lt;/p&gt;

&lt;p&gt;To avoid having such problems we can introduce a form of regularization that will discourage overly complex models with excessively large parameter values. Common regularization techniques are &lt;a href="https://www.analyticsvidhya.com/blog/2022/08/regularization-in-machine-learning/#:~:text=overfitting%20or%20underfitting.-,Regularization%20Techniques,-Ridge%20Regularization" rel="noopener noreferrer"&gt;Ridge and Lasso regularization&lt;/a&gt;, in our approach we’ll use the Ridge method. To do so we need to adjust how we calculate the error cost E and the gradient components. For the error cost we can do:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy637ytlk43uwzsapfqie.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy637ytlk43uwzsapfqie.png" alt="Regularization" width="636" height="139"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On above equation, the right added term can be interpreted as a &lt;strong&gt;penalty for complex models&lt;/strong&gt;. The β is a regularization hyperparameter that helps us to control the relative cost. Higher β implies in bigger penalties for higher model complexity, resulting in less overfitting. We also are multiplying the left term by 1/n where n is the amout of input samples, thus the number of rows of X. This term is important as it ensures that the regularization penalty is not overly influenced by the size of the dataset. The regularization term involves summing up the squared weights across all parameters. Without normalization, this sum would grow with the number of examples in your dataset. By dividing by the number of examples we ensure that the regularization term is on a consistent scale, regardless of the size of your dataset. If we don’t normalize it, the regularization term might dominate the loss term in the objective function, especially for large datasets. This could lead to the optimization process being overly sensitive to the regularization term, potentially overshadowing the impact of the actual loss term.&lt;/p&gt;

&lt;p&gt;About the gradient components we can do:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fguh652ttj1qcjrzypicc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fguh652ttj1qcjrzypicc.png" alt="Solved equation" width="450" height="201"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And that’s it :)&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;We went through a lot of points in deep details, so let’s wrap it up like a check list of “what to take care while creating a neural network to solve a supervised machine learning problem”.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Understand the problem
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;What is the problem we are trying to solve?&lt;/li&gt;
&lt;li&gt;What are the inputs of the model we need to create?&lt;/li&gt;
&lt;li&gt;What are the outputs of the model we need to create?&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Neural Network topology
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;How many dimensions our input layer requires?&lt;/li&gt;
&lt;li&gt;How many dimensions our output layer requires?&lt;/li&gt;
&lt;li&gt;Which hyperparameters needs initialization?&lt;/li&gt;
&lt;li&gt;How do we initialize weights? Random values?&lt;/li&gt;
&lt;li&gt;How do we initialize biases? Random values?
### 3. Training data&lt;/li&gt;
&lt;li&gt;Which amount of data do we need to train the model?&lt;/li&gt;
&lt;li&gt;Does the training dataset requires cleaning?&lt;/li&gt;
&lt;li&gt;How much of the dataset will be used for training and how much to check the training?&lt;/li&gt;
&lt;li&gt;How can we check if we have overfitting problems?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Reference Code
&lt;/h2&gt;

&lt;p&gt;I put a minimalist implementation of a Neural Network to solve the problem of the student trying to pass the linear algebra test, you can see the code on &lt;a href="https://github.com/buarki/supervised-machine-learning" rel="noopener noreferrer"&gt;my Github repository&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://web.njit.edu/~usman/courses/cs675_fall18/10.1.1.441.7873.pdf" rel="noopener noreferrer"&gt;Approximation by Superpositions of a Sigmoidal Function&lt;/a&gt;; G. Cybenkot.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.deep-mind.org/2023/03/26/the-universal-approximation-theorem/" rel="noopener noreferrer"&gt;The Universal Approximation Theorem&lt;/a&gt;; Alexander.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://mitsloan.mit.edu/ideas-made-to-matter/machine-learning-explained" rel="noopener noreferrer"&gt;Machine learning, explained&lt;/a&gt;; Sara Brown.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This article was originally posted on my personal site: &lt;a href="https://www.buarki.com/blog/ml-supervised-learning" rel="noopener noreferrer"&gt;https://www.buarki.com/blog/ml-supervised-learning&lt;/a&gt;&lt;/p&gt;

</description>
      <category>go</category>
      <category>tutorial</category>
      <category>ai</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>Snap out of layers: Vertical Slices for the win!</title>
      <dc:creator>Aurelio Buarque</dc:creator>
      <pubDate>Wed, 08 Nov 2023 00:22:47 +0000</pubDate>
      <link>https://dev.to/buarki/snap-out-of-layers-why-slices-are-a-better-way-to-streamline-your-code-organization-1m4f</link>
      <guid>https://dev.to/buarki/snap-out-of-layers-why-slices-are-a-better-way-to-streamline-your-code-organization-1m4f</guid>
      <description>&lt;p&gt;In the following sections of this article, we will explore alternative design approaches that address challenges introduced by using &lt;a href="https://www.oreilly.com/library/view/software-architecture-patterns/9781491971437/ch01.html" rel="noopener noreferrer"&gt;“Layered Architecture”&lt;/a&gt;. These solutions aim to make business intentions more explicit, reduce tight coupling, and improve the overall maintainability of your codebase. By the end, you will have a clearer understanding of how to structure your backend applications for greater efficiency and ease of development.&lt;/p&gt;

&lt;h2&gt;
  
  
  A case of study: how code is structured most of the time on backend applications?
&lt;/h2&gt;

&lt;p&gt;I must say that it is very common to open a brand new project and see the following structure:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frt7s7cllks6caxjgqxuu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frt7s7cllks6caxjgqxuu.png" alt="A typical folder structure of a layered architecture" width="408" height="362"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Above image represents a typical application using &lt;strong&gt;Layered Architecture&lt;/strong&gt;. This mode of organizing code focuses on organizing code by &lt;strong&gt;technical concerns&lt;/strong&gt;, such as the controllers, services, repositories and so on.&lt;/p&gt;

&lt;p&gt;I have worked on a plenty of projects using that exact same structure and they worked fine I must say. But pretty much all the time I got a new one like this I asked myself: which features is this project delivering? how it is supporting the business needs?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Organizing code by technical concerns doesn’t make it explicit which business problems the code is trying to address&lt;/strong&gt;. To figure it out you must dig into it probably starting on the controller, checking which services it calls, which repositories are used by services etc.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implications of such design
&lt;/h2&gt;

&lt;p&gt;Figuring out what such systems do usually will take some time as said before, and it’ll probably require a few code walkthroughs with a coworker that has more knowledge about it. As consequence it has a direct &lt;strong&gt;impact in the time to deliver new features or fixies on code&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Such design also pushes for &lt;strong&gt;tight coupling&lt;/strong&gt; between the system components because soon a cross-entity feature will pop up, such as “listing all tasks of an user”, and the application will end up having the situation where “user-service” is calling the “task-repository”. The intention behind it is fare and good, is called &lt;a href="https://en.wikipedia.org/wiki/Don%27t_repeat_yourself" rel="noopener noreferrer"&gt;Don’t Repeat Youself (DRY)&lt;/a&gt;. DRY principle encourages reusing code to reduce duplication. But applying it in certain scenarios, specially in cases of over-reuse, makes things so coupled that is very hard to modify a piece of code not having unintended side effects somewhere else, because as code is being shared and used to implement different features any modification done will not be isolated.&lt;/p&gt;

&lt;p&gt;Furthermore, the tight coupling between components in this traditional layered architecture can also become a significant pain point when multiple developers are collaborating on the same codebase. When different team members are working on different parts of the system that share tightly coupled dependencies, the likelihood of encountering merge conflicts significantly increases. As a consequence, developers find themselves in a race to resolve these conflicts. This race involves inspecting and manually merging conflicting code, which can be a time-consuming and error-prone process. Not only does it slow down the development process, but it also introduces the risk of introducing unintended bugs or breaking other parts of the system during conflict resolution. In essence, the tightly coupled nature of the traditional architecture exacerbates the problem of merge conflicts, making it an issue that can no longer be ignored when multiple team members are actively working on the same codebase. &lt;strong&gt;This issue can result in a domino effect, affecting the development timeline and potentially compromising code stability.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the end, the main consequence of such design is that &lt;strong&gt;business intentions gets obfuscated&lt;/strong&gt; and the comprehension of the project as a whole will rely on the deep analysis of code.&lt;/p&gt;

&lt;p&gt;As a concrete example we can assume that the features that the service from print must cover are: (1) the user needs to sign up; (2) the user needs to login; (3) user can create a task, like on Jira; (4) user can edit the task description; (5) user can delete a task; (6) and the user can list its own created tasks. If we breakdown the layered application it might look like bellow image:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiqjxxc44fbos1tinlum9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiqjxxc44fbos1tinlum9.png" alt="Typical layers organization of layered architecture" width="720" height="584"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The feature “list all user’s tasks” implemented by the “user-service” requires a query implemented by the “task-repository” which creates a coupling between them. The reason for that is: both “user-service” and “task-service” needs to do that listing, so to avoid duplicating code we make both use the same implementation (DRY principle). Now suppose that the usage of such task listing on the “task context” changes and now it should be able to list tasks of users active, inactive or both. In order to keep the “user-service” using “task-repository” this modification will also be propagated to it, and it’s easy to see where it leads us: a truly spaghetti code :)&lt;/p&gt;

&lt;h2&gt;
  
  
  A suggestion to improve code organization: Vertical Slice Architecture (VSA)
&lt;/h2&gt;

&lt;p&gt;If you consider the three layers presented as part of a big cake we could also start looking at our features as slices of it. And that way, all technical concerns needed for each feature would be grouped together ensuring minimal side effects in case of modifications. Take a look at bellow pictures:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4tlh1my9q95ipi0579e1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4tlh1my9q95ipi0579e1.png" alt="A visual representation of vertical slices architecture" width="720" height="634"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4cx6y5kk3ksw569eetjn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4cx6y5kk3ksw569eetjn.png" alt="Folder structure of application using vertical slices" width="347" height="262"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The first notable difference we must point is now the &lt;strong&gt;code structure is more close to the feature requirements&lt;/strong&gt;, actually we have a 1:1 match of how users in fact use the system and probably how the Product Owner of the team lists the available features :)&lt;/p&gt;

&lt;p&gt;By following this approach we &lt;strong&gt;enforce the system features to be treated as independent components&lt;/strong&gt; that can be created and evolve independently. We also push for a lower coupling between the system components making the &lt;strong&gt;slices more cohesive&lt;/strong&gt;. The time to grasp what the system does also decreases as the top level navigation is more related to the feature requirements per se, and one doesn’t need to understand the system as a whole, as the overall &lt;strong&gt;feature’s code now have clear boundaries&lt;/strong&gt;. And for sure, &lt;strong&gt;adding new features becomes a more straightforward exercise with a much lower risk of unintended side effects&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Even before you ask: yes, some things for sure will be shared between slices
&lt;/h2&gt;

&lt;p&gt;While reading the general idea of VSA one may think it implies the project to have nothing shared at all between slices, but that is not the case. In case you find yourself seeing duplicated code between two slices there’s no problem in extracting it into a shared package, usually called kernel or shared. But you’ll also realize that this won’t be so frequent, because for such scenario happen the piece of code to be shared must be doing pretty much the exact business related action (something expected to not happen so frequently while organizing code by features).&lt;/p&gt;

&lt;h2&gt;
  
  
  Summarize
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;There’s no silver bullet while building software&lt;/strong&gt;. From the experiences I had scaling system using vertical slice architecture I must say it gave to me and to the teams I was working on flexibility to ship new features fast and it made it easy to apply fixies and refactorings isolated with minimal side effects and &lt;strong&gt;specially during the maintenance phase&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For sure for newcomers of this design approach it will have some initial friction, but in a few iterations the general idea will stuck, and in case you need some mentoring on that you can reach me out ;)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This article was originally posted on my personal site: &lt;a href="https://www.buarki.com/blog/clear-code-with-vertical-slices" rel="noopener noreferrer"&gt;https://www.buarki.com/blog/clear-code-with-vertical-slices&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>architecture</category>
      <category>designpatterns</category>
      <category>cleancode</category>
    </item>
    <item>
      <title>Hexagonal Architecture x Onion Architecture x Clean Architecture: their differences</title>
      <dc:creator>Aurelio Buarque</dc:creator>
      <pubDate>Thu, 02 Nov 2023 07:19:05 +0000</pubDate>
      <link>https://dev.to/buarki/hexagonal-architecture-x-onion-architecture-x-clean-architecture-their-differences-4h9j</link>
      <guid>https://dev.to/buarki/hexagonal-architecture-x-onion-architecture-x-clean-architecture-their-differences-4h9j</guid>
      <description>&lt;h2&gt;
  
  
  Hexagonal Architecture
&lt;/h2&gt;

&lt;p&gt;I bet you have heard about this one, for sure. If not just google something like &lt;strong&gt;how to build an API with hexagonal architecture&lt;/strong&gt; and you'll see a plenty of tutorials, examples and Github repositories showing some examples. And I also bet that the majority of them will not explain why it has such name.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hexagonal archictecture has nothing to do with hexagons&lt;/strong&gt;. A better name for it would be the one that its author gave it: &lt;strong&gt;Ports and Adapters&lt;/strong&gt;. This pattern was proposed by &lt;strong&gt;Alistair Cockburn&lt;/strong&gt; and the &lt;a href="https://alistair.cockburn.us/hexagonal-architecture/" rel="noopener noreferrer"&gt;original publication is avaiable on his blog.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The main ideia of it is to protect the core product source code from changes on external dependencies, such as database (for sure we can discuss how frequently we change database during projects, but on other article latter on). To do so, the basic idea is: every time we detect that something is not part of the system per se we add an abstraction for it (&lt;strong&gt;a port&lt;/strong&gt;), in OOP languages it might be an interface, and then we create a concrete implementation of such abstraction, and we name this implementation &lt;strong&gt;adapter&lt;/strong&gt;, which could be a class that implements that interface and is injected into the code.&lt;/p&gt;

&lt;p&gt;Just to give you a simple and minimalist example to think about, consider a system that needs to perform queries against a database, read and write data on a cache, and fetch some data from a CMS. For each one of these needs, you could create interfaces describing how they should behave. That way, you could forget about database-specific configs, for example, and focus on coding what you need. Suppose you decide to use Postgres for production and SQLite for local development. In that case, you could create one class for each of these databases, implementing the contract established by the interface. This approach allows your application to be more flexible and maintainable in the long run.&lt;/p&gt;

&lt;h2&gt;
  
  
  Onion Architecture
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Onion Architecture&lt;/strong&gt; was proposed by &lt;strong&gt;Jeffrey Palermo&lt;/strong&gt; and you can check the &lt;a href="https://jeffreypalermo.com/2008/07/the-onion-architecture-part-1/" rel="noopener noreferrer"&gt;original article on his blog&lt;/a&gt;. It extends the ideia of protecting the system's core from the Hexagonal Architecture by introducing layers surrounding it. This core is named &lt;strong&gt;domain model&lt;/strong&gt; and it must define business rules and invariants.&lt;/p&gt;

&lt;p&gt;On top of the core we create the domain services layer which adds objects that supports enterprise services and add business rules that don't fit well into the domain model, like validations that requires some database interaction.&lt;/p&gt;

&lt;p&gt;Wrapping the domain services layer we have the &lt;strong&gt;application services&lt;/strong&gt; layer, which coordenates execution flows like a request hitting a controller what needs to call a domain-service to perform some action.&lt;/p&gt;

&lt;h2&gt;
  
  
  Clean Architecture
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Clean Architecture&lt;/strong&gt; was proposed by our dear &lt;strong&gt;Uncle Bob&lt;/strong&gt; and you can check the &lt;a href="https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html" rel="noopener noreferrer"&gt;original article on his blog&lt;/a&gt;. Summarizing, it extends the Onion Archiecture and the key difference is that domain model in this contexts is called &lt;strong&gt;entity&lt;/strong&gt; and application service is called &lt;strong&gt;use case&lt;/strong&gt;, which is good because it gives more visibility about what the application in fact does.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summarizing
&lt;/h2&gt;

&lt;p&gt;All three ideas above proposes the same thing: decouple the business rules from the framework and infrastructure-specific code, arguing that it’ll make the application flexible and maintainable in the long run.&lt;/p&gt;

</description>
      <category>cleanarch</category>
      <category>cleancode</category>
      <category>onionarchitecture</category>
      <category>hexagonalarchitectu</category>
    </item>
  </channel>
</rss>
