<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Flavio Silva</title>
    <description>I&apos;m fascinated about how tech can change people&apos;s lives and how we can tackle real-world problems with software. Hit me up using one of the channels below, and we&apos;ll chat real soon! :)</description>
    <link>https://fsilva.me/</link>
    <atom:link href="https://fsilva.me/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Tue, 26 Dec 2023 21:48:25 +0000</pubDate>
    <lastBuildDate>Tue, 26 Dec 2023 21:48:25 +0000</lastBuildDate>
    <generator>Jekyll v3.9.3</generator>
    
      <item>
        <title>🧹 Tidy First? By Kent Beck - My review and takeaways</title>
        <description>&lt;figure class=&quot;aligncenter&quot;&gt;
  &lt;img src=&quot;https://fsilva.me/images/tidy_first.jpeg&quot; alt=&quot;Front cover of the book Tidy First?&quot; /&gt;
&lt;/figure&gt;

&lt;blockquote&gt;
  &lt;p&gt;“Messy code is a nuisance. “Tidying” code, to make it more readable, requires breaking it up into manageable sections. In this practical guide, author Kent Beck, creator of Extreme Programming and pioneer of software patterns, suggests when and where you might apply tidyings to improve your code while keeping the overall structure of the system in mind. (…)” &lt;a href=&quot;https://www.oreilly.com/library/view/tidy-first/9781098151232/&quot;&gt;O’Reilly - Tidy First&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The book is clear, objective, and easy to read. Its simple explanations and examples on empirical software design carry sophisticated concepts on evaluating when, how, what, and if to tidy at all.&lt;/p&gt;

&lt;!--more--&gt;

&lt;blockquote&gt;
  &lt;p&gt;TL;DR: The book is an 8/10 and I recommend it to any programmer at any level of experience. It’s not a 10/10 because the first section about actual tydings with practical examples felt a bit unnecessary. I understand why it’s there, but the discussions presented in sections 2 and 3 were way more valuable for me. It might be the other way around depending on the experience though.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;sections&quot;&gt;Sections&lt;/h2&gt;

&lt;p&gt;The author split the book into three sections: Tidyngs, Managing, and Theory.&lt;/p&gt;

&lt;h3 id=&quot;tydings&quot;&gt;Tydings&lt;/h3&gt;

&lt;p&gt;According to Kent Beck, tidyings are a subset of refactoring. Tydings are the cute, fuzzy little refactorings that nobody could possibly hate on. In this section, the author presents several tidyings that can be done to leave things better than before, e.g. adjusting the order based on reading or cohesion, improving comments, etc.&lt;/p&gt;

&lt;p&gt;For people with little or no experience with refactorings, this is going to probably be the most valuable part.
Personally, the “nobody could possibly hate on” piece, is the highlight of this section. It helped me to create a mental model to identify the line between a tidy and a “full-blown” refactor that would require much more work and discussion.&lt;/p&gt;

&lt;h3 id=&quot;managing&quot;&gt;Managing&lt;/h3&gt;

&lt;p&gt;“Tyding is geek self-care” - Kent Beck, Tidy First?&lt;/p&gt;

&lt;p&gt;Here is where things started to become more interesting for me. This section addresses when to start and stop tidying, and more importantly how to combine tyding, changing the structure of the code, with changing the behavior of the system.&lt;/p&gt;

&lt;p&gt;In this section is the reason why I’d love to change the title of this book is “First, After, Later, Never”. It provides a simple and objective guideline to decide when and if to tidy something. I won’t spoil it here so you can read this part from the book and reach your conclusions.&lt;/p&gt;

&lt;h3 id=&quot;theory&quot;&gt;Theory&lt;/h3&gt;

&lt;p&gt;This was my favorite section and every chapter was very interesting to read, digest, and process. Particularly the correlation between software and cash flow versus options. This analogy helped me to think more structurally about the real cost of software, its development, and its evolution.&lt;/p&gt;

&lt;p&gt;Another highlight of this section is having a clear distinction between behavior and structural changes. Where the behavior changes are the ones related to what the software does and structural changes are the ones that support what the software needs to do. E.g. software for HR offers features like payroll, holiday planning, etc. These features are the behaviors, anything else to support these features is structural, such as the code organization, how data is stored, retrieved, etc. Understanding this distinction, their costs, and values helps to better judge when to assume debt and plan to pay/tidy.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;This book is the first of a trilogy. The upcoming books are not yet released (as of the date of writing - 22 December 2023), and they will extend the scope of changes and impact. The first one is focused on the individual programmer (you), the second will be focused on the team, and the third will focus on all stakeholders.&lt;/p&gt;
</description>
        <pubDate>Tue, 26 Dec 2023 13:00:00 +0000</pubDate>
        <link>https://fsilva.me/tidy-first-review.html</link>
        <guid isPermaLink="true">https://fsilva.me/tidy-first-review.html</guid>
        
        <category>book</category>
        
        <category>programming</category>
        
        <category>thougths</category>
        
        <category>review</category>
        
        
      </item>
    
      <item>
        <title>🐔 What happened with my project on the Rinha de Backend challenge</title>
        <description>&lt;figure class=&quot;aligncenter&quot;&gt;
  &lt;img src=&quot;https://fsilva.me/images/rooster_fight.png&quot; alt=&quot;rooster fight&quot; /&gt;
&lt;/figure&gt;

&lt;p&gt;I participated in the &lt;a href=&quot;https://github.com/zanfranceschi/rinha-de-backend-2023-q3&quot;&gt;Rinha de backend (pt-BR)&lt;/a&gt; or &lt;a href=&quot;https://en.wikipedia.org/wiki/Cockfight&quot;&gt;“Backend Rooster Fight” in English&lt;/a&gt; challenge and I was super excited to see the results of my super hacky and performant solution.&lt;/p&gt;

&lt;p&gt;The results were scheduled to be published at 21:00 BRT (2 AM CEST my local time). So I stayed up til later that night grabbed popcorn, and waited for the results.&lt;/p&gt;

&lt;p&gt;Well, I wish I hadn’t waited…&lt;/p&gt;

&lt;!--more--&gt;

&lt;blockquote&gt;
  &lt;p&gt;TL;DR; My solution didn’t run on the official test server because I built the docker image to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;linux/arm64&lt;/code&gt; instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;linux/amd64&lt;/code&gt; 🤦🤦🤦 . I fixed the issue and ran the same tests afterward and I reached ~50% of points of the winner, mainly because in my solution I didn’t tolerate any risk of losing writes. Checkout my repository &lt;a href=&quot;https://github.com/flavio1110/rinha-de-backend&quot;&gt;&lt;strong&gt;flavio1110/rinha-de-backend&lt;/strong&gt;&lt;/a&gt; with my solution.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;what-was-the-rinha-de-backend&quot;&gt;What was the Rinha de Backend?&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Before we get into the details, a brief explanation of what was the challenge.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;From July 28th to August 25th, the Backend Fight was held, a tournament in which the API that supported the most load during a stress test would be the winner. Participants had to implement an API with endpoints to create, query and search for ‘people’ (a kind of CRUD without UPDATE and DELETE). In the tournament, participants still had to deal with CPU and memory restrictions – each participant had to deliver the API in docker-compose format and could only use 1.5 CPU units and 3GB of memory. More details on technical aspects can be found in the instructions) – &lt;a href=&quot;https://github.com/zanfranceschi/rinha-de-backend-2023-q3/tree/main#o-que-%C3%A9&quot;&gt;(translated from its repository in English)&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;On top of the description above, some things important to highlight are:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;You can write your API with any language or framework, as long as you can build a docker image out of it&lt;/li&gt;
  &lt;li&gt;The solution has to run two instances of the API in parallel.&lt;/li&gt;
  &lt;li&gt;The requests will be balanced as you wish between these instances via Nginx.&lt;/li&gt;
  &lt;li&gt;You have to use one of the following data stores: MySQL, PostgreSQL, or MongoDB.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It was a super fun, informal, practical, and a very good opportunity to exercise and learn new things.&lt;/p&gt;

&lt;h4 id=&quot;how-the-winners-were-determined&quot;&gt;How the winners were determined?&lt;/h4&gt;

&lt;p&gt;To make the comparison easier and fun, the &lt;strong&gt;single parameter&lt;/strong&gt; used to determine the winner of the challenge was the &lt;strong&gt;number of people inserted&lt;/strong&gt; into the database.&lt;/p&gt;

&lt;h2 id=&quot;how-did-i-do&quot;&gt;How did I do?&lt;/h2&gt;

&lt;figure class=&quot;aligncenter&quot;&gt;
  &lt;img src=&quot;https://fsilva.me/images/rinha-2023-q3-meme.jpeg&quot; alt=&quot;my solution explodes&quot; /&gt;
 &lt;figcaption&gt;POV you are looking at me while I check the results.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;I didn’t. I was disqualified because the container with my APIs didn’t start. Therefore I got nothing. 🤦🤦🤦&lt;/p&gt;

&lt;h3 id=&quot;wrong-platform&quot;&gt;Wrong platform?&lt;/h3&gt;

&lt;p&gt;Yes, that’s true. My solution didn’t even run because &lt;a href=&quot;https://github.com/zanfranceschi/rinha-de-backend-2023-q3/blob/main/resultados/primeira-fase/flavio1110/docker-compose.logs#L63&quot;&gt;it failed to start the APIs&lt;/a&gt;. I built the images for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;linux/arm64&lt;/code&gt; instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;linux/amd64&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It was a mistake on my end, and it made me super disappointed. I ran the tests on the &lt;a href=&quot;https://github.com/flavio1110/rinha-de-backend/actions/runs/5949338191&quot;&gt;CI pipelines&lt;/a&gt;, and it gave me confidence the image was properly built and I didn’t double-check the requirements. Shame on me.&lt;/p&gt;

&lt;p&gt;Well, I was disqualified but I was curious to see how it would perform on a server with the &lt;a href=&quot;https://github.com/zanfranceschi/rinha-de-backend-2023-q3/blob/main/misc/lshw-aws&quot;&gt;same configuration as the one used for the official tests&lt;/a&gt;. So, I fixed the platform of the image, spun up an EC2 with the same configuration, and finally ran the tests. The results were a bit disappointing compared to the TOP 10.&lt;/p&gt;

&lt;h3 id=&quot;how-did-i-approach-the-problem&quot;&gt;How did I approach the problem?&lt;/h3&gt;

&lt;p&gt;I had two constraints in mind while designing and building:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;It has to work – each endpoint has to do what it is supposed to do.&lt;/li&gt;
  &lt;li&gt;Keep things as simple as possible.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Each of these points brought a few tradeoffs that ended up limiting the overall performance of the solution.&lt;/p&gt;

&lt;p&gt;You can check my solution on &lt;a href=&quot;https://github.com/flavio1110/rinha-de-backend&quot;&gt;flavio1110/rinha-de-backend&lt;/a&gt;. It’s built with Go and PostgreSQL.&lt;/p&gt;

&lt;p&gt;Given many APIs were performing super well, a few days before the last day of the challenge, &lt;a href=&quot;https://github.com/zanfranceschi/rinha-de-backend-2023-q3/commit/be637c944434bdddfe4a92a31bd17cd528f68a38&quot;&gt;the load for the stress test was doubled&lt;/a&gt;, and it had a massive impact on the performance of my API. You can check the comparison below:&lt;/p&gt;

&lt;p&gt;Before…&lt;/p&gt;

&lt;figure class=&quot;aligncenter&quot;&gt;
  &lt;a href=&quot;https://fsilva.me/rinha-de-backend/rinhabackendsimulation-20230821183911665/&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://fsilva.me/images/gh_results_before.png&quot; alt=&quot;rooster fight&quot; /&gt;&lt;/a&gt;
 &lt;figcaption&gt;Print of part of the report generated by Gatling of my solution run on Github BEFORE the load was doubled.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;After…&lt;/p&gt;

&lt;figure class=&quot;aligncenter&quot;&gt;
  &lt;a href=&quot;https://fsilva.me/rinha-de-backend/rinhabackendsimulation-20230823091057409/&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://fsilva.me/images/gh_results_after.png&quot; alt=&quot;rooster fight&quot; /&gt;&lt;/a&gt;
 &lt;figcaption&gt;Print of part of the report generated by Gatling of my solution run on Github AFTER the load was doubled.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h3 id=&quot;what-were-the-bottlenecks&quot;&gt;What were the bottlenecks?&lt;/h3&gt;

&lt;p&gt;The inserts were the bottleneck. Because I didn’t tolerate the risk of losing writes and wanted to keep it simple, I missed the opportunity to introduce a distributed mechanism to validate the data before trying to insert it into the database and perform the inserts in batch.&lt;/p&gt;

&lt;h4 id=&quot;validating-the-uniqueness-of-a-field-apelido-nickname&quot;&gt;Validating the uniqueness of a field &lt;em&gt;Apelido&lt;/em&gt; (Nickname)&lt;/h4&gt;

&lt;p&gt;On my designed solution, without a distributed cache, it was not possible to perform without the DB in 100% of the cases.&lt;/p&gt;

&lt;p&gt;I had an in-memory cache, but if the nickname wasn’t there because it wasn’t there because the entry was inserted via the other instance, that request would reach the DB, occupy a connection, take resources, etc.&lt;/p&gt;

&lt;p&gt;Introducing a distributed cache like &lt;a href=&quot;https://redis.uptrace.dev/&quot;&gt;Redis&lt;/a&gt; would enable me to add and check the entry in a single place. As a result, we could decrease the memory necessary to run the APIs and move to the cache.&lt;/p&gt;

&lt;h4 id=&quot;batching-inserts&quot;&gt;Batching inserts&lt;/h4&gt;

&lt;p&gt;I inserted each entry per a valid request because I didn’t want to risk losing valid writes in case there was any error between the construction of the batch and its execution.&lt;/p&gt;

&lt;p&gt;This was a huge problem because it used too many DB connections and resources, and slowed down the creation request.&lt;/p&gt;

&lt;h3 id=&quot;did-i-like-it&quot;&gt;Did I like it?&lt;/h3&gt;

&lt;p&gt;Despite my results, I loved the challenge! I learned quite some tricks with nginx config and some insteresting stuff like using &lt;a href=&quot;https://redis.io/docs/interact/pubsub/&quot;&gt;Redis&lt;/a&gt; or even &lt;a href=&quot;https://www.postgresql.org/docs/current/sql-notify.html&quot;&gt;PostgreSQL&lt;/a&gt; as a “PubSub”.
The interaction with the community via &lt;a href=&quot;https://twitter.com/flavio1110&quot;&gt;Twitter&lt;/a&gt; and Github was super nice!
I also liked the fact of working with something challenging and closer to reality. Loved it!&lt;/p&gt;

&lt;h3 id=&quot;what-could-i-have-done-differently&quot;&gt;What could I have done differently?&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Build the image using the correct platform.&lt;/li&gt;
  &lt;li&gt;A challenge is a challenge. I should have been more flexible with the writing.&lt;/li&gt;
  &lt;li&gt;Tweak the Nginx configuration to load balance based on fewer connections and disable logs.&lt;/li&gt;
  &lt;li&gt;Use a distributed cache and drop the read table.&lt;/li&gt;
  &lt;li&gt;Batch inserts.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;whats-next&quot;&gt;What’s next?&lt;/h2&gt;

&lt;p&gt;Check the &lt;a href=&quot;https://github.com/zanfranceschi/rinha-de-backend-2023-q3#resultados&quot;&gt;official results&lt;/a&gt; and look into the repositories of the participants. I guarantee I’ll learn something new.&lt;/p&gt;

&lt;p&gt;Stay tuned on &lt;a href=&quot;https://twitter.com/rinhadebackend&quot;&gt;@rinhadebackend&lt;/a&gt; for the next challenges, I can’t wait for the next ones.&lt;/p&gt;

&lt;p&gt;Other than that, I’ll apply the lessons learned to my solution and hopefully get better results. Watch &lt;a href=&quot;https://github.com/flavio1110/&quot;&gt;flavio1110/rinha-de-backend&lt;/a&gt; and see how it will evolve.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This challenge is officially over, but you can still do it. Challenge accepted?&lt;/em&gt;&lt;/p&gt;
</description>
        <pubDate>Mon, 28 Aug 2023 13:00:00 +0000</pubDate>
        <link>https://fsilva.me/rinha-de-backend.html</link>
        <guid isPermaLink="true">https://fsilva.me/rinha-de-backend.html</guid>
        
        <category>challenge</category>
        
        <category>development</category>
        
        <category>docker</category>
        
        <category>go</category>
        
        <category>postgresql</category>
        
        
      </item>
    
      <item>
        <title>🔥 Test DB integrations with Testcontainers</title>
        <description>&lt;figure class=&quot;aligncenter&quot;&gt;
  &lt;img src=&quot;https://golang.testcontainers.org/logo.png&quot; alt=&quot;Testcontainers logo&quot; /&gt;
&lt;/figure&gt;

&lt;p&gt;Picture this, you have a critical and reasonably complicated piece of logic in your application that is handled in the database. Despite any change on it (or around it), you must have a 100% guarantee that piece continues to work just fine. So, what do you do?&lt;/p&gt;

&lt;!--more--&gt;

&lt;h3 id=&quot;back-in-the-ancient-days&quot;&gt;Back in the ancient days…&lt;/h3&gt;

&lt;figure class=&quot;aligncenter&quot;&gt;
  &lt;img src=&quot;https://fsilva.me/images/caveofhands.webp&quot; alt=&quot;Ancient cave art of many hand prints.&quot; /&gt;
  &lt;figcaption&gt; Ancient cave art of many hand prints. (Credit: Petr Kratochvila/Shutterstock)&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Before the “age of containers”, the solution would be a variant of the following:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Have a database with the schema aligned with the application’s version.&lt;/li&gt;
  &lt;li&gt;Ensure your CI tool has access to that database, to run the tests in your CI pipeline.&lt;/li&gt;
  &lt;li&gt;Create a script to arrange the necessary data for the test.&lt;/li&gt;
  &lt;li&gt;Write your testing using the DB.&lt;/li&gt;
  &lt;li&gt;Create a script to revert any DB changes performed by the test, such as inserts, updates, etc.
    &lt;blockquote&gt;
      &lt;p&gt;&lt;a href=&quot;https://avatao.com/blog-life-before-docker-and-beyond/&quot; target=&quot;\_blank&quot;&gt;This is&lt;/a&gt; a very nice post about the Docker: Life Before and after.&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h5 id=&quot;whats-the-problem-with-testing-with-a-shared-database&quot;&gt;What’s the problem with testing with a shared database?&lt;/h5&gt;

&lt;p&gt;Simply put, it’s complex, expensive to maintain, and has many other reasons to fail. For example:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The DB’s schema version is not the same as the application’s version you want to test.&lt;/li&gt;
  &lt;li&gt;There is a change in the network policy that blocks access to the database.&lt;/li&gt;
  &lt;li&gt;Someone’s test setup corrupted the data that you expected to have, so you arrange script fails, or the test fails because of missing data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because of the cost and flakiness of such tests, in many cases, the &lt;strong&gt;&lt;em&gt;solution&lt;/em&gt;&lt;/strong&gt; was to move logic from DB to the application’s code when performance was not a problem, or just manually test it from time to time and hope it doesn’t break because of an unforeseen reason (it-never-happened-in-the-last-30-minutes).&lt;/p&gt;

&lt;h3 id=&quot;containers-everywhere&quot;&gt;Containers everywhere&lt;/h3&gt;

&lt;p&gt;With the popularization of containers, not only developing, runnings, and deploying became easier, but also integrating and testing your external dependencies became easier, faster, and more dependable.&lt;/p&gt;

&lt;figure class=&quot;aligncenter&quot;&gt;
  &lt;img src=&quot;https://fsilva.me/images/containers-everywhere.webp&quot; alt=&quot;Meme Buzzlightier saying to Wood: Containers Everywhere&quot; /&gt;
&lt;/figure&gt;

&lt;p&gt;We can now effortlessly run not only the regular dependencies, like RDBMSs and Message Brokers, but we can emulate many cloud provider services using tools like &lt;a href=&quot;https://docs.localstack.cloud/overview/&quot; target=&quot;\_blank&quot;&gt;localstack&lt;/a&gt;.&lt;/p&gt;

&lt;h4 id=&quot;what-about-testing-testcontainers-to-rescue&quot;&gt;What about testing? Testcontainers to rescue!&lt;/h4&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;a href=&quot;https://golang.testcontainers.org/&quot; target=&quot;_blank&quot;&gt;Testcontainers&lt;/a&gt; makes it simple to create and clean up container-based dependencies for automated integration/smoke tests. The clean, easy-to-use API enables developers to programmatically define containers that should be run as part of a test and clean up those resources when the test is done.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It supports many popular languages/frameworks like &lt;a href=&quot;https://golang.testcontainers.org/&quot; target=&quot;\_blank&quot;&gt;Go&lt;/a&gt;, &lt;a href=&quot;https://testcontainers.org/&quot; target=&quot;\_blank&quot;&gt;Java&lt;/a&gt;, &lt;a href=&quot;https://dotnet.testcontainers.org/&quot; target=&quot;\_blank&quot;&gt;.NET&lt;/a&gt;, &lt;a href=&quot;https://docs.rs/testcontainers/latest/testcontainers/&quot; target=&quot;\_blank&quot;&gt;Rust&lt;/a&gt;, and others. You can check the full list on its &lt;a href=&quot;https://www.testcontainers.org/&quot; target=&quot;\_blank&quot;&gt;official&lt;/a&gt; site](https://www.testcontainers.org/) and because it’s open source, you check out and contribute to it in &lt;a href=&quot;https://github.com/testcontainers&quot; target=&quot;\_blank&quot;&gt;its repositories&lt;/a&gt;.&lt;/p&gt;

&lt;h4 id=&quot;how-to-use-testcontainers-on-integration-tests&quot;&gt;How to use Testcontainers on integration tests?&lt;/h4&gt;

&lt;p&gt;For this example, assume that one of the features of your software is to search for users based on a few criteria. Your users are stored in a &lt;a href=&quot;https://www.postgresql.org/&quot; target=&quot;\_blank&quot;&gt;PostgreSQL&lt;/a&gt; database and you query them using &lt;a href=&quot;https://en.wikipedia.org/wiki/SQL&quot; target=&quot;\_blank&quot;&gt;SQL&lt;/a&gt;. Something like:&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;searchPeople&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sql&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;params&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;searchParams&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;([]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;person&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;query&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;`select first_name, last_name, city
		 from people
		 where ($1::text is null or first_name = $1)
		   and ($2::text is null or last_name = $2)
		   and ($3::text is null or city = $3)
		 order by first_name asc`&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;people&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;person&lt;/span&gt;

  &lt;span class=&quot;c&quot;&gt;//Execute query, check errors, populate people variable&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;people&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The search criteria will evolve, by adding new filters or adding new features like pagination. Therefore, we need to make sure that whenever we touch this query its existing behavior is not broken. Let’s write a test for it using Testcontainers.&lt;/p&gt;

&lt;h3 id=&quot;writing-the-test&quot;&gt;Writing the test&lt;/h3&gt;

&lt;p&gt;To be able to run these tests against a real database, we need to:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Start the DB&lt;/li&gt;
  &lt;li&gt;Create the schema&lt;/li&gt;
  &lt;li&gt;Arrange data, Act, and Assert&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let’s see in detail how to perform each one of these steps.&lt;/p&gt;

&lt;h4 id=&quot;1-start-the-db&quot;&gt;1. Start the DB&lt;/h4&gt;

&lt;p&gt;Let’s create a function that will start a DB, return its connection string and a function to terminate it when we are done.&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;startTestDB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;testing&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;envVars&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;s&quot;&gt;&quot;POSTGRES_USER&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;     &lt;span class=&quot;s&quot;&gt;&quot;user&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;s&quot;&gt;&quot;POSTGRES_PASSWORD&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;super-secret&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;s&quot;&gt;&quot;POSTGRES_DB&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;       &lt;span class=&quot;s&quot;&gt;&quot;people&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;s&quot;&gt;&quot;PORT&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;              &lt;span class=&quot;s&quot;&gt;&quot;5432/tcp&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;n&quot;&gt;getConnString&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;host&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;port&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nat&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Port&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Sprintf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;postgres://%s:%s@%s:%s/%s?sslmode=disable&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;envVars&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;POSTGRES_USER&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;envVars&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;POSTGRES_PASSWORD&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Port&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;envVars&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;POSTGRES_DB&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;n&quot;&gt;req&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;testcontainers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ContainerRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;Image&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;        &lt;span class=&quot;s&quot;&gt;&quot;postgres:14&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;ExposedPorts&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;envVars&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;PORT&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]},&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;Env&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;          &lt;span class=&quot;n&quot;&gt;envVars&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;WaitingFor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;   &lt;span class=&quot;n&quot;&gt;wait&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ForSQL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nat&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Port&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;envVars&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;PORT&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]),&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;pgx&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getConnString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;WithStartupTimeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Second&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;pgC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;testcontainers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GenericContainer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;testcontainers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GenericContainerRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;ContainerRequest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;Started&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;          &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Errorf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;failed to start db container :%w&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pgC&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MappedPort&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;5432/tcp&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Errorf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;failed to get mapped port :%w&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pgC&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Host&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Errorf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;failed to get host :%w&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;n&quot;&gt;connString&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Sprintf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;postgres://%s:%s@%s:%d/%s?sslmode=disable&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;envVars&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;POSTGRES_USER&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;envVars&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;POSTGRES_PASSWORD&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;envVars&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;POSTGRES_DB&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;

	&lt;span class=&quot;n&quot;&gt;terminate&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;testing&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pgC&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Terminate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Fatalf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;failed to terminate container: %s&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;connString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;terminate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The code above is self-explanatory if you are familiar with Go, but there are a few aspects that I’d like to highlight:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Testcontainers will map and assign a random port. Therefore, in this case, instead of connecting to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5432&lt;/code&gt;, it’s necessary to connect to the assigned port. This is similar to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;host&lt;/code&gt;name. Therefore, to get your correct connection string it’s needed to resolve &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;port&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;host&lt;/code&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;The container will automatically shut down when the application is finished. However, it is recommended to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;defer&lt;/code&gt; its termination as soon as it is started. For that, a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;terminate&lt;/code&gt; func is returned by the method above.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So far our test looks like this:&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TestSearchPeople&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;testing&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;connString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;terminate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;startTestDB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;defer&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;terminate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

	&lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;openDB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;connString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Fatal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;fail to open DB&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;c&quot;&gt;// Continue&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;2-create-the-schema&quot;&gt;2. Create the schema&lt;/h4&gt;

&lt;p&gt;With a DB up and running we can initiate it with the expected schema, a.k.a migrate the DB schema.&lt;/p&gt;

&lt;p&gt;In the real world, we would use something more sophisticated like &lt;a href=&quot;https://github.com/golang-migrate/migrate&quot;&gt;go-migrate&lt;/a&gt;, but for the sake of simplicity let’s write something simpler.&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;migrateDB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sql&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ExecContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;create table if not exists people (first_name text,last_name text,city text)&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Errorf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;failed to migrate DB: %w&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;3-arrange-data-act-and-assert&quot;&gt;3. Arrange data, Act, and Assert&lt;/h4&gt;

&lt;p&gt;The last step before the actual test is to arrange the data. We could accomplish it together with the schema migration, however keeping these two parts separate helps to keep each test isolated, avoiding side effects when the data is tweaked.&lt;/p&gt;

&lt;p&gt;For our tests, we need to insert 4 different people with distinct names and cities. Then we can use this data to act and assert the results.
This is what the final test func looks like, testing every variation of our query.&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TestSearchPeople&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;testing&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
	&lt;span class=&quot;c&quot;&gt;// Start the DB&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;connString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;terminate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;startTestDB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;c&quot;&gt;// Terminate the container when the func TestSearchPeople finishes&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;defer&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;terminate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

	&lt;span class=&quot;c&quot;&gt;// Open the the database&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;openDB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;connString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Fatal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;fail to open DB&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;c&quot;&gt;// Migrate schema&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;migrateDB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Fatal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;fail to migrate DB&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;n&quot;&gt;insert&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;`insert into people values
			(&apos;Flavio&apos;, &apos;Silva&apos;, &apos;Olbia&apos;),
			(&apos;Joost&apos;, &apos;Van Huis&apos;, &apos;Amsterdam&apos;),
			(&apos;Aldben&apos;, &apos;Arimeritin&apos;, &apos;Istambul&apos;),
			(&apos;Nando&apos;, &apos;Pelect&apos;, &apos;Perth&apos;);`&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;// Arrange data&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ExecContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;insert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Fatal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;fail to insert initial data&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;c&quot;&gt;// Several test cases&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;without filters&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;testing&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;people&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;searchPeople&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;searchParams&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{})&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NoError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Equal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;people&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;

	&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;filtering by first name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;testing&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;people&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;searchPeople&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;searchParams&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;firstName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;strPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Joost&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)})&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NoError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Equal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;people&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Equal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Joost&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;people&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;firstName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;filtering by last name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;testing&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;people&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;searchPeople&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;searchParams&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lastName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;strPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Arimeritin&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)})&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NoError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Equal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;people&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Equal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Arimeritin&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;people&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lastName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;filtering by city&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;testing&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;people&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;searchPeople&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;searchParams&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;city&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;strPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Olbia&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)})&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NoError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Equal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;people&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Equal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Olbia&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;people&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;city&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With this test, we have the guarantee the covered scenarios are working. Profit!&lt;/p&gt;

&lt;p&gt;You can check the results by running the tests.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;go &lt;span class=&quot;nb&quot;&gt;test&lt;/span&gt; ./... &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; RUN   TestSearchPeople
2023/05/09 15:48:55 github.com/testcontainers/testcontainers-go - Connected to docker:
  Server Version: 20.10.22
  API Version: 1.41
  Operating System: Docker Desktop
  Total Memory: 3932 MB
2023/05/09 15:48:55 Starting container &lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;: 95dc2928dae3 image: docker.io/testcontainers/ryuk:0.3.4
2023/05/09 15:48:56 Waiting &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;container &lt;span class=&quot;nb&quot;&gt;id &lt;/span&gt;95dc2928dae3 image: docker.io/testcontainers/ryuk:0.3.4
2023/05/09 15:48:56 Container is ready &lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;: 95dc2928dae3 image: docker.io/testcontainers/ryuk:0.3.4
2023/05/09 15:48:56 Starting container &lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;: adedca079bc6 image: postgres:14
2023/05/09 15:48:57 Waiting &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;container &lt;span class=&quot;nb&quot;&gt;id &lt;/span&gt;adedca079bc6 image: postgres:14
2023/05/09 15:48:58 Container is ready &lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;: adedca079bc6 image: postgres:14
&lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; RUN   TestSearchPeople/without_filters
&lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; RUN   TestSearchPeople/filtering_by_first_name
&lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; RUN   TestSearchPeople/filtering_by_last_name
&lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; RUN   TestSearchPeople/filtering_by_city
&lt;span class=&quot;nt&quot;&gt;---&lt;/span&gt; PASS: TestSearchPeople &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;3.48s&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;---&lt;/span&gt; PASS: TestSearchPeople/without_filters &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;0.00s&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;---&lt;/span&gt; PASS: TestSearchPeople/filtering_by_first_name &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;0.00s&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;---&lt;/span&gt; PASS: TestSearchPeople/filtering_by_last_name &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;0.00s&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;---&lt;/span&gt; PASS: TestSearchPeople/filtering_by_city &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;0.00s&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
PASS
ok  	github.com/flavio1110/go-for-csharp-devs/experiments/test-db-interactions	.724s
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can check the source code of this test on my &lt;a href=&quot;https://github.com/flavio1110/go-for-csharp-devs/tree/main/experiments/test-db-interactions&quot; target=&quot;\_blank&quot;&gt;experiments repository&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;final-thoughts&quot;&gt;Final thoughts&lt;/h3&gt;

&lt;p&gt;Despite Testcontainers reducing the cost of integration tests and making them more reliable, it doesn’t remove the cost entirely. The tests will take more time to run (especially if where you are running the tests doesn’t have the image downloaded yet).&lt;/p&gt;

&lt;p&gt;Nevertheless, in my opinion, the price is low compared to the benefit that it yields.&lt;/p&gt;

&lt;p&gt;What about you? How do you write integration tests, and what are your main challenges?&lt;/p&gt;
</description>
        <pubDate>Mon, 17 Apr 2023 13:00:00 +0000</pubDate>
        <link>https://fsilva.me/test-db-integrations-with-testcontainers.html</link>
        <guid isPermaLink="true">https://fsilva.me/test-db-integrations-with-testcontainers.html</guid>
        
        <category>tests</category>
        
        <category>database</category>
        
        <category>tools</category>
        
        <category>docker</category>
        
        
      </item>
    
      <item>
        <title>🔥 Improve your Frontend skills with Frontend Mentor</title>
        <description>&lt;figure class=&quot;aligncenter&quot;&gt;
  &lt;video autoplay=&quot;&quot; loop=&quot;&quot; muted=&quot;&quot; playsinline=&quot;&quot;&gt;
    &lt;source src=&quot;https://fsilva.me/images/design-responsive.mp4&quot; type=&quot;video/mp4&quot; /&gt;
  &lt;/video&gt;
&lt;/figure&gt;

&lt;p&gt;When studying HTML, CSS, and Javascript it’s hard to practice with real-world examples, so oftentimes we get stuck trying to come up with a good design and then building it.&lt;/p&gt;

&lt;h3 id=&quot;frontend-mentor-to-the-recue&quot;&gt;Frontend Mentor to the recue!&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://www.frontendmentor.io/&quot;&gt;Frontend Mentor&lt;/a&gt; offers front-end coding challenges and interesting projects to practice your HTML, CSS, and JavaScript. It is as close as it gets to work on a professional real-world project.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;After creating your account, you can start one of its &lt;a href=&quot;https://www.frontendmentor.io/challenges&quot;&gt;challeges&lt;/a&gt;. There are many free challenges that can get you started and give you plenty of things to play with. However, if you want take one step further, have access to premium challenges, figma and sketch design files, and private solutions, you can subscribe to the &lt;a href=&quot;https://www.frontendmentor.io/pro#pricing&quot;&gt;pro developer&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;how-does-it-work&quot;&gt;How does it work?&lt;/h3&gt;

&lt;p&gt;Once you start a challenge, you will download a zip for that challenge. For example the challenge &lt;a href=&quot;https://www.frontendmentor.io/challenges/four-card-feature-section-weK1eFYK&quot;&gt;Four card feature section&lt;/a&gt;, will give you a zip file containing the instructions, necessary images, basic design details, and the most important the design images for desktop and mobile!&lt;/p&gt;

&lt;figure class=&quot;aligncenter&quot;&gt;
  &lt;img src=&quot;https://fsilva.me/images/four-card-feature-files.webp&quot; alt=&quot;File tree for Four cards feature section challenge&quot; /&gt;
&lt;/figure&gt;

&lt;p&gt;These files are all you need to start hacking your front end!&lt;/p&gt;

&lt;p&gt;After the challenge is complete you can submit it and get feedback from the community! That’s excellent!&lt;/p&gt;

&lt;p&gt;I’ve been playing with some challenges myself, (the image of the banner is from &lt;a href=&quot;https://github.com/flavio1110/frontendmentor/tree/main/four-card-feature-section-master&quot;&gt;my own implementation&lt;/a&gt; of the Four Cards feature section).&lt;/p&gt;

&lt;p&gt;I’m not a Frontend specialist by no means, but you can find my solutions so far on &lt;a href=&quot;https://github.com/flavio1110/frontendmentor&quot;&gt;my GitHub repository&lt;/a&gt;.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;What about you? Do you know any other tool that offers such an immersive and near-real-world experience?&lt;/p&gt;
</description>
        <pubDate>Tue, 11 Apr 2023 13:00:00 +0000</pubDate>
        <link>https://fsilva.me/improve-your-fe-skills-with-frontendmentor.html</link>
        <guid isPermaLink="true">https://fsilva.me/improve-your-fe-skills-with-frontendmentor.html</guid>
        
        <category>frontend</category>
        
        <category>tools</category>
        
        <category>learning</category>
        
        
      </item>
    
      <item>
        <title>🐉 got: Embracing my lazyness with my custom git CLI</title>
        <description>&lt;figure class=&quot;aligncenter&quot;&gt;
  &lt;img src=&quot;https://raw.githubusercontent.com/flavio1110/got/main/got.png&quot; alt=&quot;The eyes of Drogon&quot; /&gt;
&lt;/figure&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/flavio1110/got&quot; target=&quot;\_blank&quot;&gt;got&lt;/a&gt; is a CLI written in Go, created on top of the git CLI, to make my life easier by shortening some commands I use daily.&lt;/p&gt;

&lt;p&gt;Because it’s built on top of git, all git commands will also work just fine and I don’t need to keep switching between &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;got&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git&lt;/code&gt;!&lt;/p&gt;

&lt;p&gt;If I want to clean up my local dead branches I can &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;got rmb&lt;/code&gt; instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git branch | grep -v &quot;main&quot; | xargs git branch -D&lt;/code&gt; (you can call me lazy. I accept that 😅).&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://xkcd.com/1319/&quot; target=&quot;\_blank&quot;&gt;&lt;img src=&quot;https://imgs.xkcd.com/comics/automation_2x.png&quot; alt=&quot;xckd 1319&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;a href=&quot;https://xkcd.com/1319/&quot; target=&quot;\_blank&quot;&gt;xkcd&lt;/a&gt; Automation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;!--more--&gt;

&lt;h3 id=&quot;but-why&quot;&gt;But… Why?&lt;/h3&gt;

&lt;p&gt;My first experience with a Distributed version control system (DVCS), was many years ago with &lt;a href=&quot;https://en.wikipedia.org/wiki/Mercurial&quot; target=&quot;\_blank&quot;&gt;mercurial hg&lt;/a&gt;, and despite taking some time to get used to the new way of working as compared to a centralized version control system, I got very used to &lt;a href=&quot;https://gist.github.com/cortesben/016cd401faae5a8dae59&quot; target=&quot;\_blank&quot;&gt;its short commands&lt;/a&gt;, aliases, and simplicity.
We could do something like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hg pull&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hg pul&lt;/code&gt; (that’s not a typo. This is an actual alias for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hg pull&lt;/code&gt;), we were able to close a branch and commit a message in a single command like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hg commit --close-branch -m &apos;closing this branch&apos;&lt;/code&gt;. It was super nice and handy!
Then, eventually, I started working with &lt;a href=&quot;https://git-scm.com/&quot; target=&quot;\_blank&quot;&gt;git&lt;/a&gt; and I was amazed by its differences, possibilities, and features. However, it felt more verbose for day-to-day tasks. So I ended up creating a bunch of aliases for the commands that I use more often, or commands that are longer and I always have to google it to remember.
Sneak peek of my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.zshrc&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;c&quot;&gt;#...&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;alias &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;git push origin head&apos;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;alias stat&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;git status -s&apos;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;alias &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;gbr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;git branch | grep -v &quot;&lt;/span&gt;main&lt;span class=&quot;s2&quot;&gt;&quot; | xargs git branch -D&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;alias &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;pick&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;git cherry-pick&quot;&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# and a few more...&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#...&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;blockquote&gt;
  &lt;p&gt;Speaking on &lt;a href=&quot;https://ohmyz.sh/&quot; target=&quot;\_blank&quot;&gt;zsh&lt;/a&gt;, if you use it with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;go&lt;/code&gt; plugin, you will have a conflict with the alias &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;got&lt;/code&gt; for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;go test&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The aliases work so well and are easy to maintain… But I wanted something fancier and all have the opportunity to go through the process of writing a CLI in Go.&lt;/p&gt;

&lt;h3 id=&quot;how&quot;&gt;How&lt;/h3&gt;

&lt;p&gt;Got is built using &lt;a href=&quot;https://github.com/spf13/cobra&quot; target=&quot;\_blank&quot;&gt;cobra&lt;/a&gt;, which makes the work so much easier. Cobra has its own &lt;a href=&quot;https://github.com/spf13/cobra-cli/blob/main/README.md&quot; target=&quot;\_blank&quot;&gt;CLI called Cobra Generator&lt;/a&gt; that helps bootstrap your CLI project and add commands. You can check the full documentation &lt;a href=&quot;https://github.com/spf13/cobra-cli/blob/main/README.md&quot; target=&quot;\_blank&quot;&gt;here&lt;/a&gt;, but here goes the basic usage:&lt;/p&gt;

&lt;h4 id=&quot;install&quot;&gt;Install&lt;/h4&gt;

&lt;p&gt;With go installed, open the terminal and execute the following command:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;go &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;github.com/spf13/cobra-cli@latest
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;booststraping-your-cli&quot;&gt;Booststraping your CLI&lt;/h4&gt;

&lt;p&gt;Navigate to a folder that you want to have your project, init a go module, then execute the cobra init. e.g&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;go mod init my-cli
cobra-cli init
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Voilá! You have your custom CLI, ad you can run it with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;go run .&lt;/code&gt;! You will see a result like:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;A longer description that spans multiple lines and likely contains
examples and usage of using your application. For example:

Cobra is a CLI library &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can now add a command with executing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cobra-cli add ping&lt;/code&gt;, then execute the command again. You will see a result like:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;A longer description that spans multiple lines and likely contains
examples and usage of using your application. For example:

Cobra is a CLI library &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.

Usage:
  my-cli &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;

Available Commands:
  completion  Generate the autocompletion script &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;the specified shell
  &lt;span class=&quot;nb&quot;&gt;help        &lt;/span&gt;Help about any &lt;span class=&quot;nb&quot;&gt;command
  &lt;/span&gt;ping        A brief description of your &lt;span class=&quot;nb&quot;&gt;command

&lt;/span&gt;Flags:
  &lt;span class=&quot;nt&quot;&gt;-h&lt;/span&gt;, &lt;span class=&quot;nt&quot;&gt;--help&lt;/span&gt;     &lt;span class=&quot;nb&quot;&gt;help &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;my-cli
  &lt;span class=&quot;nt&quot;&gt;-t&lt;/span&gt;, &lt;span class=&quot;nt&quot;&gt;--toggle&lt;/span&gt;   Help message &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;toggle

Use &lt;span class=&quot;s2&quot;&gt;&quot;my-cli [command] --help&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;more information about a command.
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The foundation for your CLI is in place, you &lt;em&gt;only&lt;/em&gt; need to worry about the actual logic of the commands because &lt;a href=&quot;https://github.com/spf13/cobra&quot; target=&quot;\_blank&quot;&gt;cobra&lt;/a&gt; will take care of all the pumbling for you.&lt;/p&gt;

&lt;p&gt;You can check several examples in the &lt;a href=&quot;https://git{:target=&amp;quot;_blank&amp;quot;},hub.com/spf13/cobra/blob/main/user_guide.md&quot;&gt;cobra&lt;/a&gt;&lt;a href=&quot;https://git{:target=&amp;quot;_blank&amp;quot;},hub.com/spf13/cobra/blob/main/user_guide.md&quot; target=&quot;\_blank&quot;&gt; documentation&lt;/a&gt;, and use it as a base to create your shine CLI.&lt;/p&gt;

&lt;h3 id=&quot;ok-what-about-the-logic-in-got&quot;&gt;Ok, what about the logic in got?&lt;/h3&gt;

&lt;p&gt;In got I have two ways of executing some logic. 1) using &lt;a href=&quot;https://github.com/go-git/go-git&quot; target=&quot;\_blank&quot;&gt;go-git&lt;/a&gt; to perform some actions like iterate in all local branches and delete all except main, and 2) executing a git cli command from my Go application directly.&lt;/p&gt;

&lt;h4 id=&quot;go-git&quot;&gt;go-git&lt;/h4&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;a href=&quot;https://github.com/go-git/go-git&quot; target=&quot;\_blank&quot;&gt;go-git&lt;/a&gt; is a highly extensible git implementation library written in pure Go. It can be used to manipulate git repositories at low level (plumbing) or high level (porcelain), through an idiomatic Go API. - (Description from its repo).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It is such a well-documented, flexible, and powerful lib. I highly recommend looking into it if you even thought to do something with git, or if you intend to create your own lib. 10/10!&lt;/p&gt;

&lt;p&gt;The example below stages all files (including untracked) and commit them:&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Getwd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;// get the current path&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;exitIfError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;git&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PlainOpen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;// open the repository related to the path&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;exitIfError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Worktree&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;// get the current worktree&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;exitIfError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;// Stage all files&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;exitIfError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Commit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;commit yay!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;git&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CommitOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{})&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;// commit&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;exitIfError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I have to say that for basic operations like this one, we could be better served by executing the git cli directly like in the example below. However, &lt;em&gt;go-git&lt;/em&gt; opens several possibilities like in the support of several type of storage, such as in-memory, file system, or anything you can think of as long as you implement the &lt;a href=&quot;https://pkg.go.dev/github.com/go-git/go-git/v5/plumbing/storer&quot; target=&quot;\_blank&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Storer&lt;/code&gt;&lt;/a&gt; interface.
At this moment I’m not using such features yet, but I’m planning to use them for some commands like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;got squash&lt;/code&gt; that will squash all commits of the current branch.&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;n&quot;&gt;cmd&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Command&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;git&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;add&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;-A&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;cmd&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Stdout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Stdout&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;cmd&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Stderr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Stdout&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cmd&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;


&lt;span class=&quot;n&quot;&gt;cmd&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Command&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;git&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;commit&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;-m&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;commit yay!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;cmd&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Stdout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Stdout&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;cmd&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Stderr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Stdout&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cmd&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;fallback-to-git&quot;&gt;Fallback to git&lt;/h4&gt;

&lt;p&gt;It is important because I wanted to “proxy” all unknown &lt;em&gt;got&lt;/em&gt; commands to &lt;em&gt;git&lt;/em&gt;. In this way, I can use &lt;em&gt;got&lt;/em&gt; for my custom commands and the standard &lt;em&gt;git&lt;/em&gt; commands without worrying about what is available where.
The other benefit is I don’t need to implement things that are good enough or I don’t use so often.&lt;/p&gt;

&lt;p&gt;Implementing it was fairly simple.
&lt;em&gt;The snippet below is part of the &lt;a href=&quot;https://github.com/flavio1110/got/blob/main/cmd/root.go&quot; target=&quot;\_blank&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;root.go&lt;/code&gt;&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rootCmd&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;fallbackToGit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fallbackToGit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;cmd&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Command&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;git&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;cmd&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Stdout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Stdout&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;cmd&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Stderr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Stdout&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cmd&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;// Skipping error here, because git already sends it to stdout, and I don&apos;t have anything else to do with it.&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;conclusion-and-whats-next&quot;&gt;Conclusion and what’s next?&lt;/h3&gt;

&lt;p&gt;This small project started as an experiment for playing with writing a custom CLI, but I ended up creating something that is very useful for me. Win-win!&lt;/p&gt;

&lt;p&gt;I still have a few commands I want to introduce in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;got&lt;/code&gt;, but my next step is to create a reasonable test suit, so I can have confidence that things work as they suppose to. This is especially important given it is responsible for my interaction with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git&lt;/code&gt;. That’s a big deal!&lt;/p&gt;

&lt;p&gt;Anyways, I created it to attend to my laziness, but if &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;got&lt;/code&gt; looks interesting to you, feel free to use, fork, and contribute. 💪&lt;/p&gt;

&lt;p&gt;I hope this post can help you to see how easy is to write a CLI using Go, and maybe can inspire you to identify things that you do daily and can be somehow optimized.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Till next time o/&lt;/p&gt;
</description>
        <pubDate>Thu, 30 Mar 2023 13:00:00 +0000</pubDate>
        <link>https://fsilva.me/embracing-my-lazyness-with-a-custom-git-cli.html</link>
        <guid isPermaLink="true">https://fsilva.me/embracing-my-lazyness-with-a-custom-git-cli.html</guid>
        
        <category>go</category>
        
        <category>thougths</category>
        
        <category>tools</category>
        
        
      </item>
    
      <item>
        <title>📺 YouTube: Suggestions #1</title>
        <description>&lt;figure class=&quot;aligncenter&quot;&gt;
    &lt;img src=&quot;https://fsilva.me/images/youtube.webp&quot; alt=&quot;A hand holding a smartphone while youtube app is open &quot; /&gt;
&lt;/figure&gt;

&lt;p&gt;We live in an unprecedented era in which we can learn pretty much anything we want for free (&lt;em&gt;or paying very little depending on how much patience you have with ads&lt;/em&gt;).&lt;/p&gt;

&lt;p&gt;YouTube is undoubtedly one of the best sources of free content. The only challenge is really to find good channels where you learn stuff and have a good time while doing it.&lt;/p&gt;

&lt;p&gt;This is the first issue of a series of posts in which I’m going to recommend three channels with excellent videos.&lt;/p&gt;

&lt;h2 id=&quot;techworld-with-nana&quot;&gt;TechWorld with Nana&lt;/h2&gt;

&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;a href=&quot;https://www.youtube.com/@TechWorldwithNana&quot; target=&quot;_blank&quot;&gt;YouTube Channel&lt;/a&gt;&lt;/td&gt;
      &lt;td&gt;&lt;a href=&quot;https://twitter.com/Njuchi_&quot; target=&quot;\_blank&quot;&gt;Twitter&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;This is my favorite channel for DevOps-related topics. Nana’s didactics and real-world examples help a lot to get to know new topics or deep dive into a specific subject.
This 4-hour long k8s full course is just one of many others. &lt;a href=&quot;https://www.youtube.com/@TechWorldwithNana&quot; target=&quot;\_blank&quot;&gt;Check it out&lt;/a&gt;.&lt;/p&gt;

&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/X48VuDVv0do&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

&lt;p&gt;&lt;!--more--&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;matt-pocock&quot;&gt;Matt Pocock&lt;/h2&gt;

&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;a href=&quot;https://www.youtube.com/@mattpocockuk&quot; target=&quot;\_blank&quot;&gt;YouTube Channel&lt;/a&gt;&lt;/td&gt;
      &lt;td&gt;&lt;a href=&quot;https://twitter.com/mattpocockuk&quot; target=&quot;\_blank&quot;&gt;Twitter&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Advanced TypeScript wizardry, plus on-the-day updates from the latest TypeScript releases (and other open-source loveliness). &lt;a href=&quot;https://www.youtube.com/@mattpocockuk&quot; target=&quot;\_blank&quot;&gt;Check it out&lt;/a&gt;&lt;/p&gt;

&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/EU0TB_8KHpY&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;fireship&quot;&gt;Fireship&lt;/h2&gt;

&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;a href=&quot;https://www.youtube.com/@Fireship&quot; target=&quot;\_blank&quot;&gt;YouTube Channel&lt;/a&gt;&lt;/td&gt;
      &lt;td&gt;&lt;a href=&quot;https://twitter.com/fireship_dev&quot; target=&quot;\_blank&quot;&gt;Twitter&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;The Gateway Drug for developers. This is how the channel describes itself. 😅&lt;/p&gt;

&lt;p&gt;The super funny weekly code report and the 100 seconds pills of knowledge about many different things are very informative and entertaining. &lt;a href=&quot;https://www.youtube.com/@Fireship&quot; target=&quot;\_blank&quot;&gt;Check it out&lt;/a&gt;.&lt;/p&gt;

&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/nMdwS3A6zck&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;What about you? What other YouTube channels do you watch to get up to speed?&lt;/em&gt;&lt;/p&gt;
</description>
        <pubDate>Tue, 28 Mar 2023 13:00:00 +0000</pubDate>
        <link>https://fsilva.me/youtube-suggestions-1.html</link>
        <guid isPermaLink="true">https://fsilva.me/youtube-suggestions-1.html</guid>
        
        <category>learning</category>
        
        <category>devops</category>
        
        <category>kubernetes</category>
        
        <category>typescript</category>
        
        <category>youtube</category>
        
        
      </item>
    
      <item>
        <title>📂 Go: Importig a CSV to PostgreSQL</title>
        <description>&lt;figure class=&quot;aligncenter&quot;&gt;
    &lt;img src=&quot;https://fsilva.me/images/csv-rainbow.webp&quot; alt=&quot;contents of a CSV file with each column in a different color&quot; /&gt;
&lt;/figure&gt;

&lt;p&gt;If you are using Go ad PostgreSQL, and need to performa a bulk import a CSV,
it’s most likely you will find the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;COPY&lt;/code&gt; protocol is the &lt;a href=&quot;https://www.postgresql.org/docs/current/sql-copy.html&quot; target=&quot;\_blank&quot;&gt;feature&lt;/a&gt; that suits you better.
In that direction, you will find examples using &lt;a href=&quot;https://github.com/jackc/pgx&quot;&gt;pgx&lt;/a&gt; &lt;a href=&quot;https://pkg.go.dev/github.com/jackc/pgx/v4#Conn.CopyFrom&quot; target=&quot;\_blank&quot;&gt;CopyFrom&lt;/a&gt; that relies on the native protocol, and it’s fairly easy to use.
&lt;strong&gt;However, depending how it’s used you can have an exponencial increase of memory consumption of your application making it unreliable and more expensive to run.&lt;/strong&gt;&lt;/p&gt;

&lt;!--more--&gt;

&lt;blockquote&gt;
  &lt;p&gt;TL;DR;: &lt;strong&gt;Don’t&lt;/strong&gt; load the file in memory and use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pgx.CopyFromRows&lt;/code&gt;. Instead, use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;io.Reader&lt;/code&gt; of the file and implement a custom &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pgx.CopyFromSource&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/flavio1110/large-csv-to-pgsql&quot; target=&quot;\_blank&quot;&gt;Checkout the repository with examples and details presented here&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;context&quot;&gt;Context&lt;/h3&gt;

&lt;p&gt;Most of the examples out there, are either using &lt;a href=&quot;https://pkg.go.dev/github.com/jackc/pgx/v4#CopyFromRows&quot; target=&quot;\_blank&quot;&gt;CopyFromRows&lt;/a&gt; or &lt;a href=&quot;https://pkg.go.dev/github.com/jackc/pgx/v4#CopyFromSlice&quot; target=&quot;\_blank&quot;&gt;CopyFromSlice&lt;/a&gt;. However, the big problem is these two options require you to have the entire content in memory to use.
This is not a big deal when dealing with small files, there won’t be concurrent usage, or you have infinite memory 😅.&lt;/p&gt;

&lt;h3 id=&quot;how-big-is-the-problem&quot;&gt;How big is the problem?&lt;/h3&gt;

&lt;p&gt;Comparing the memory consumption for the two distinct approaches importing a file with ~16MB (1M rows).&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Approach/metric&lt;/th&gt;
      &lt;th&gt; &lt;/th&gt;
      &lt;th style=&quot;text-align: right&quot;&gt;TotalAlloc&lt;/th&gt;
      &lt;th&gt; &lt;/th&gt;
      &lt;th style=&quot;text-align: right&quot;&gt;Sys&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Stream file&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;61 MiB&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;12 Mib&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Read entire file&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;84 MiB&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;58 Mib&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;&lt;strong&gt;+37.70%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;&lt;strong&gt;+346.15%&lt;/strong&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;&lt;strong&gt;Yes,&lt;/strong&gt; you read it right! using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CopyFromRows&lt;/code&gt; obtained +346.15% of memory from the OS! 58 MiB instead of 12 MiB.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;You can read more about the meaning of each metric on &lt;a href=&quot;https://golang.org/pkg/runtime/#MemStats&quot;&gt;https://golang.org/pkg/runtime/#MemStats&lt;/a&gt; and find the source code and details of the comparisson on &lt;a href=&quot;https://github.com/flavio1110/large-csv-to-pgsql&quot; target=&quot;\_blank&quot;&gt;this repository&lt;/a&gt;..&lt;/em&gt;&lt;/p&gt;

&lt;h3 id=&quot;whats-the-most-efficient-way-for-using-the-copy-protocol-with-pgx&quot;&gt;What’s the most efficient way for using the COPY protocol with pgx?&lt;/h3&gt;

&lt;p&gt;Instead of reading the entire in memory, the idea is to stream each line of the file directly to PostgreSQL. In this way, we only need to keep the current line in-memory as opposed to the entire file.&lt;/p&gt;

&lt;h4 id=&quot;how-can-we-do-it&quot;&gt;How can we do it?&lt;/h4&gt;

&lt;p&gt;The &lt;a href=&quot;https://github.com/jackc/pgx/blob/master/copy_from.go#LL238C21-L238C21&quot; target=&quot;\_blank&quot;&gt;CopyFrom&lt;/a&gt; method receives an implementation of the interface &lt;a href=&quot;https://github.com/jackc/pgx/blob/master/copy_from.go#L68&quot; target=&quot;\_blank&quot;&gt;CopyFromSource&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let’s implement this interface using a CSV file with the followng three columns: first_name, last_name, and city.&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newPeopleCopyFromSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;csvStream&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Reader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;peopleCopyFromSource&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;csvReader&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;csv&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewReader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;csvStream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;csvReader&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReuseRecord&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;// reuse slice to return the record line by line&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;csvReader&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FieldsPerRecord&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;

   &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;peopleCopyFromSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
       &lt;span class=&quot;n&quot;&gt;reader&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;csvReader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
       &lt;span class=&quot;n&quot;&gt;isBOF&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;  &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;// first line is header&lt;/span&gt;
       &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;make&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([]&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;interface&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{},&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;peopleColumns&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)),&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;peopleCopyFromSource&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;reader&lt;/span&gt;        &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;csv&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Reader&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;           &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;currentCsvRow&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt;        &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;interface&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;isEOF&lt;/span&gt;         &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;isBOF&lt;/span&gt;         &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pfs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;peopleCopyFromSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Values&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;([]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pfs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;isEOF&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
       &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

   &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pfs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
       &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pfs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

   &lt;span class=&quot;c&quot;&gt;// the order of the elements of the record array, must match with&lt;/span&gt;
   &lt;span class=&quot;c&quot;&gt;// the order of the columns in passed into the copy method&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;pfs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pfs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;currentCsvRow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;pfs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pfs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;currentCsvRow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;pfs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pfs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;currentCsvRow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
   &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pfs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pfs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;peopleCopyFromSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;pfs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;currentCsvRow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pfs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pfs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reader&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
   &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pfs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

       &lt;span class=&quot;c&quot;&gt;// when get to the end of the file return false and clean the error.&lt;/span&gt;
       &lt;span class=&quot;c&quot;&gt;// If it&apos;s io.EOF we can&apos;t return an error&lt;/span&gt;
       &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Is&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pfs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EOF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
           &lt;span class=&quot;n&quot;&gt;pfs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;isEOF&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
           &lt;span class=&quot;n&quot;&gt;pfs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;
       &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
       &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

   &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pfs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;isBOF&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
       &lt;span class=&quot;n&quot;&gt;pfs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;isBOF&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;
       &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pfs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

   &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pfs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;peopleCopyFromSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pfs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can now use this implementation in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CopyFrom&lt;/code&gt; method. e.g.&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pgxConn&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CopyFrom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pgx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Identifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;people&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;peopleColumns&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newPeopleCopyFromSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;csvStream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h3&gt;

&lt;p&gt;Using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CopyFrom&lt;/code&gt; with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CopyFromRows&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CopyFrom&lt;/code&gt; will significantly increase the memory comsultion of your application. The high memory usage can bring several problems like OOM errors, increase of costs, unavailability, etc.&lt;/p&gt;

&lt;p&gt;By using a custom implementation of &lt;a href=&quot;https://github.com/jackc/pgx/blob/master/copy_from.go#L68&quot; target=&quot;\_blank&quot;&gt;CopyFromSource&lt;/a&gt; will make your application much more efficient, reliable, and cheaper to ru.&lt;/p&gt;

&lt;p&gt;You can find the entire source code of the examples above on &lt;a href=&quot;https://github.com/flavio1110/large-csv-to-pgsql&quot; target=&quot;\_blank&quot;&gt;this repository&lt;/a&gt;. There you will also find more deatils about the comparisson and the not-so-great implementation.&lt;/p&gt;
</description>
        <pubDate>Sun, 26 Mar 2023 13:00:00 +0000</pubDate>
        <link>https://fsilva.me/csv-import-go-postgresql.html</link>
        <guid isPermaLink="true">https://fsilva.me/csv-import-go-postgresql.html</guid>
        
        <category>go</category>
        
        <category>programming</category>
        
        <category>gotchas</category>
        
        <category>postgresql</category>
        
        
      </item>
    
      <item>
        <title>🔢 Go: Where is the decimal type?</title>
        <description>&lt;figure class=&quot;aligncenter&quot;&gt;
    &lt;img src=&quot;https://fsilva.me/images/gopher-side-eye.webp&quot; alt=&quot;Gopher side-eye&quot; /&gt;
&lt;/figure&gt;

&lt;p&gt;&lt;strong&gt;Go&lt;/strong&gt; doesn’t have a primitive &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;decimal&lt;/code&gt; type for arbitrary-precision fixed-point decimal numbers. Yes, you read it right. Therefore, if you need to deal with fixed-point precision there are two main options:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Use an external package like &lt;a href=&quot;https://github.com/shopspring/decimal&quot;&gt;decimal&lt;/a&gt;, which introduces the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;decimal&lt;/code&gt; type. However, the current version (1.3.1), can “only” represent numbers with a maximum of 2^31 digits after the decimal point.&lt;/li&gt;
  &lt;li&gt;Use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;int64&lt;/code&gt; to store and deal with these numbers. For e.g. given you need 6 precision digits, therefore &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;79.23&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;23.00&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;54.123456&lt;/code&gt;, become respectively &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;79230000&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;23000000&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;54123456&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is an &lt;a href=&quot;https://github.com/golang/go/issues/19787#issue-218228389&quot;&gt;open proposal&lt;/a&gt; to add decimal float types (IEEE 754-2008) in the std lib. However, for now, it’s just a proposal being discussed, without guarantee it will be ever added.&lt;/p&gt;
</description>
        <pubDate>Tue, 21 Mar 2023 13:00:00 +0000</pubDate>
        <link>https://fsilva.me/go-decimal-type.html</link>
        <guid isPermaLink="true">https://fsilva.me/go-decimal-type.html</guid>
        
        <category>go</category>
        
        <category>programming</category>
        
        <category>gotchas</category>
        
        
      </item>
    
      <item>
        <title>🧠 Go resources for beginners</title>
        <description>&lt;figure class=&quot;aligncenter&quot;&gt;
    &lt;img src=&quot;https://fsilva.me/images/gophers.webp&quot; alt=&quot;Gopher side-eye&quot; /&gt;
&lt;/figure&gt;

&lt;p&gt;Below a living list of compiled links for whoever is learning &lt;strong&gt;Go&lt;/strong&gt;.&lt;/p&gt;

&lt;h3 id=&quot;official-docs&quot;&gt;Official docs&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Effective Go&lt;/strong&gt; - &lt;a href=&quot;https://go.dev/doc/effective_go&quot;&gt;https://go.dev/doc/effective_go&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;A document that gives tips for writing clear, idiomatic Go code. A must-read for any new Go programmer. It augments the tour and the language specification, both of which should be read first.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Go Code Review Comments&lt;/strong&gt; &lt;a href=&quot;https://github.com/golang/go/wiki/CodeReviewComment&quot;&gt;https://github.com/golang/go/wiki/CodeReviewComment&lt;/a&gt;&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;This page collects common comments made during reviews of Go code, so that a single detailed explanation can be referred to by shorthands. This is a laundry list of common mistakes, not a comprehensive style guide. You can view this as a supplement to Effective Go.&lt;/li&gt;
    &lt;/ul&gt;

    &lt;!--more--&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Go Docs&lt;/strong&gt; - &lt;a href=&quot;https://go.dev/doc/&quot;&gt;https://go.dev/doc/&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;Root for many useful documentations&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;FAQ&lt;/strong&gt; - &lt;a href=&quot;https://go.dev/doc/faq&quot;&gt;https://go.dev/doc/faq&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;Answers to common questions about Go.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;blogs&quot;&gt;Blogs&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Dave Cheney&lt;/strong&gt; - &lt;a href=&quot;https://dave.cheney.net/&quot;&gt;https://dave.cheney.net/&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;Specially the &lt;a href=&quot;https://dave.cheney.net/practical-go&quot;&gt;practical go section&lt;/a&gt;, it has TONS of good advice and real-world examples of how to deal with daily challenges. I highly recommend it.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;courses&quot;&gt;Courses&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;The way to go&lt;/strong&gt; - &lt;a href=&quot;https://www.educative.io/courses/the-way-to-go&quot;&gt;https://www.educative.io/courses/the-way-to-go&lt;/a&gt;&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;It’s a course from educative.io, it goes from the basics concepts of the language to more advanced ones. It also brings very interesting insights into the differences between the approaches of Java/C# to Go..&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;How to code in go&lt;/strong&gt; - &lt;a href=&quot;https://www.digitalocean.com/community/tutorial_series/how-to-code-in-go&quot;&gt;https://www.digitalocean.com/community/tutorial_series/how-to-code-in-go&lt;/a&gt;&lt;/p&gt;
    &lt;ul&gt;
      &lt;li&gt;A great collection of tutorials that cover basic Go concepts, ideal for beginers&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;videos&quot;&gt;Videos&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Go in 100 seconds&lt;/strong&gt; - &lt;a href=&quot;https://www.youtube.com/watch?v=446E-r0rXHI&amp;amp;t=38s&quot;&gt;https://www.youtube.com/watch?v=446E-r0rXHI&amp;amp;t=38s&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;Short introduction about Go&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Simplicity is complicated&lt;/strong&gt; - &lt;a href=&quot;https://www.youtube.com/watch?v=rFejpH_tAHM&quot;&gt;https://www.youtube.com/watch?v=rFejpH_tAHM&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;Rob Pike talks about how Go is often described as a simple language. It is not, it just seems that way. Rob explains how Go’s simplicity hides a great deal of complexity, and that both the simplicity and complexity are part of the design.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Concurrency is not parallelism&lt;/strong&gt; - &lt;a href=&quot;https://www.youtube.com/watch?v=qmg1CF3gZQ0&amp;amp;t=1582s&quot;&gt;https://www.youtube.com/watch?v=qmg1CF3gZQ0&amp;amp;t=1582s&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;Rob Pike talks about concurrency and how Go implements it&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Understanding channels&lt;/strong&gt; - &lt;a href=&quot;https://www.youtube.com/watch?v=KBZlN0izeiY&amp;amp;t=1011s&quot;&gt;https://www.youtube.com/watch?v=KBZlN0izeiY&amp;amp;t=1011s&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;Channels provide a simple mechanism for goroutines to communicate, and a powerful construct to build sophisticated concurrency patterns. We will delve into the inner workings of channels and channel operations, including how they’re supported by the runtime scheduler and memory&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Concurrency in Go&lt;/strong&gt; - &lt;a href=&quot;https://www.youtube.com/watch?v=\_uQgGS_VIXM&amp;amp;list=PLsc-VaxfZl4do3Etp_xQ0aQBoC-x5BIgJ&quot;&gt;https://www.youtube.com/watch?v=\_uQgGS_VIXM&amp;amp;list=PLsc-VaxfZl4do3Etp_xQ0aQBoC-x5BIgJ&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;Playlist with a few short videos about different components of concurrency in Go&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Just for func&lt;/strong&gt; - &lt;a href=&quot;https://www.youtube.com/watch?v=H_4eRD8aegk&amp;amp;list=PL64wiCrrxh4Jisi7OcCJIUpguV_f5jGnZ&quot;&gt;https://www.youtube.com/watch?v=H_4eRD8aegk&amp;amp;list=PL64wiCrrxh4Jisi7OcCJIUpguV_f5jGnZ&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;A very complete playlist of tutorials given by Francesc Campoy, a past Developer Advocate for the Go team at Google, that cover simple to advanced topics in Go&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Golang crash course&lt;/strong&gt; - &lt;a href=&quot;https://www.youtube.com/watch?v=SqrbIlUwR0U&quot;&gt;https://www.youtube.com/watch?v=SqrbIlUwR0U&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;A 90 minutes video that covers most of Go features with cristal clear live coding examples, excelent for beginers to get a fast gist of Go&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;podcasts&quot;&gt;Podcasts&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Go Time&lt;/strong&gt; - &lt;a href=&quot;https://open.spotify.com/show/2cKdcxETn7jDp7uJCwqmSE&quot;&gt;Spotify&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;Diverse discussions from around the Go and its community&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 19 Mar 2023 13:00:00 +0000</pubDate>
        <link>https://fsilva.me/go-resources.html</link>
        <guid isPermaLink="true">https://fsilva.me/go-resources.html</guid>
        
        <category>go</category>
        
        <category>programming</category>
        
        <category>learning</category>
        
        
      </item>
    
      <item>
        <title>👋 Welcome</title>
        <description>&lt;figure class=&quot;aligncenter&quot;&gt;
    &lt;img src=&quot;https://fsilva.me/images/code-banner.webp&quot; alt=&quot;Gopher side-eye&quot; /&gt;
&lt;/figure&gt;

&lt;p&gt;Welcome, here I’ll be sharing my discoveries, studies, and experiences in the software industry. As a software engineer, I’ve had the opportunity to work on a variety of projects and technologies, and I’ve learned a lot along the way.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;My goal in creating this blog is to document my journey and share what I’ve learned with others. Whether you’re a seasoned developer or just starting out, I hope that the insights and knowledge I share will be useful and valuable to you.&lt;/p&gt;

&lt;p&gt;In this blog, you can expect to find posts about various topics related to software engineering, such as programming languages, frameworks, tools, best practices, and more. I’ll also share my thoughts on the latest trends and developments in the industry.&lt;/p&gt;

&lt;p&gt;Above all, I hope that this blog will inspire you to continue learning and growing as a software engineer. Thanks for stopping by, and I look forward to sharing my experiences with you.&lt;/p&gt;
</description>
        <pubDate>Sun, 19 Mar 2023 13:00:00 +0000</pubDate>
        <link>https://fsilva.me/welcome.html</link>
        <guid isPermaLink="true">https://fsilva.me/welcome.html</guid>
        
        <category>thougths</category>
        
        
      </item>
    
  </channel>
</rss>
