<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Juan Vega</title>
    <description>The latest articles on Forem by Juan Vega (@juanvegadev).</description>
    <link>https://forem.com/juanvegadev</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F141711%2Fadd58eef-3934-4b7a-87c1-ab9043a287be.jpg</url>
      <title>Forem: Juan Vega</title>
      <link>https://forem.com/juanvegadev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/juanvegadev"/>
    <language>en</language>
    <item>
      <title>A tip to get a nice git history: let git tell your code story</title>
      <dc:creator>Juan Vega</dc:creator>
      <pubDate>Fri, 07 Jul 2023 11:03:56 +0000</pubDate>
      <link>https://forem.com/juanvegadev/tips-to-get-a-nice-git-history-let-git-tell-your-code-story-1p3a</link>
      <guid>https://forem.com/juanvegadev/tips-to-get-a-nice-git-history-let-git-tell-your-code-story-1p3a</guid>
      <description>&lt;p&gt;If you're new to git, it can be overwhelming to manage your code changes and keep your git history clean. In this post, we'll explore some tips to help you get a nice git history that tells your code story effectively.&lt;/p&gt;

&lt;h2&gt;
  
  
  Avoid merge commits
&lt;/h2&gt;

&lt;p&gt;Merge commits can make your git history messy and hard to follow. Instead, you can use git rebase to keep your git history linear and make it easier to understand. With git rebase, you can merge changes from one branch onto another and create a clean and concise git history.&lt;/p&gt;

&lt;h2&gt;
  
  
  All in one, interactive rebase
&lt;/h2&gt;

&lt;p&gt;To use git rebase, you can use the interactive mode (&lt;code&gt;-i&lt;/code&gt;) to squash multiple commits into a single commit with a new message. For example, you can run the command &lt;code&gt;git rebase -i HEAD~3&lt;/code&gt; to squash the last 4 commits into a new one with a new message. This will open up an editor where you can choose which commits to squash and edit the commit message. &lt;a href="https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/incorporating-changes-from-a-pull-request/about-pull-request-merges#squash-and-merge-your-commits" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; and &lt;a href="https://docs.gitlab.com/ee/user/project/merge_requests/squash_and_merge.html" rel="noopener noreferrer"&gt;GitLab&lt;/a&gt; also support squash and merge operation to create a single commit with all your Pull/Merge Request changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use conventional commit
&lt;/h2&gt;

&lt;p&gt;Conventional commit is a specification for adding human and machine-readable meaning to commit messages. By using a conventional commit format, you can make your commit messages more informative and easier to understand. To learn more about conventional commit, you can check out &lt;a href="https://www.conventionalcommits.org/en/v1.0.0/" rel="noopener noreferrer"&gt;this post&lt;/a&gt; which explains what it is and how to use it effectively.&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/juanvegadev" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F141711%2Fadd58eef-3934-4b7a-87c1-ab9043a287be.jpg" alt="juanvegadev"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/juanvegadev/convetional-commit-a-meaningful-commit-message-proposal-4n1o" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Convetional Commit: A meaningful commit message proposal&lt;/h2&gt;
      &lt;h3&gt;Juan Vega ・ Oct 14 '22&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#tutorial&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#git&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;With these tips, you can create a clean and informative git history that tells the story of your code changes effectively. Happy coding!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.pexels.com/photo/depth-of-field-photography-of-brown-tree-logs-923167/" rel="noopener noreferrer"&gt;cover from pexels&lt;/a&gt;&lt;/p&gt;

</description>
      <category>career</category>
      <category>beginners</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Convetional Commit: A meaningful commit message proposal</title>
      <dc:creator>Juan Vega</dc:creator>
      <pubDate>Fri, 14 Oct 2022 12:09:13 +0000</pubDate>
      <link>https://forem.com/juanvegadev/convetional-commit-a-meaningful-commit-message-proposal-4n1o</link>
      <guid>https://forem.com/juanvegadev/convetional-commit-a-meaningful-commit-message-proposal-4n1o</guid>
      <description>&lt;p&gt;Could you say how many commits the repositories you usually work with have altogether? I don't know, probably hundreds of thousands. Furthermore, most of them have a message that will never be read, but if you need to navigate the history. I ensure you will thank a clear structure with a concrete and explicit statement describing the commit purpose.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you write a commit?
&lt;/h2&gt;

&lt;p&gt;What rules do you usually enforce while writing a commit message? Have you checked if all people on your team follow the same? And what about developers within your company or your tech stack community? I had never thought about this topic until I needed to navigate git history and package release looking for compatibility issues.&lt;/p&gt;

&lt;p&gt;Of course, this is not something I invented or that only happened to me. If you have good versioning following SemVer supported by clear commit messages, you can easily detect breaking changes affecting a bug at first sight.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's agree on how to write a commit message
&lt;/h2&gt;

&lt;p&gt;Based on the Angular community commit guidelines, we have &lt;a href="https://www.conventionalcommits.org/en/v1.0.0/"&gt;Convetional Commits&lt;/a&gt;, a simple yet powerful specification to write clear messages for both automated tools and humans. I already talked about it in my last post:&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/juanvegadev" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--oMqzlXe_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--oZ_R_q4F--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_150%2Cq_auto%2Cw_150/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/141711/add58eef-3934-4b7a-87c1-ab9043a287be.jpg" alt="juanvegadev"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/juanvegadev/configure-the-release-of-your-golang-module-with-github-actions-nb2" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Configure the release of your golang module with Github Actions&lt;/h2&gt;
      &lt;h3&gt;Juan Vega ・ Oct 9 ・ 2 min read&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#go&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#tutorial&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#beginners&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;The idea is to have three sections within the commit headline and then a body and a footer of the message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;type&amp;gt;[optional scope]: &amp;lt;description&amp;gt;

[optional body]

[optional footer(s)]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Please go and read the &lt;a href="https://www.conventionalcommits.org/en/v1.0.0/#examples"&gt;examples&lt;/a&gt;. You will notice how natural it feels.&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>git</category>
    </item>
    <item>
      <title>Configure the release of your golang module with Github Actions</title>
      <dc:creator>Juan Vega</dc:creator>
      <pubDate>Sun, 09 Oct 2022 17:44:12 +0000</pubDate>
      <link>https://forem.com/juanvegadev/configure-the-release-of-your-golang-module-with-github-actions-nb2</link>
      <guid>https://forem.com/juanvegadev/configure-the-release-of-your-golang-module-with-github-actions-nb2</guid>
      <description>&lt;p&gt;Building and distributing a golang module is relatively easy. You need to create a GitHub repository and push your code. That's all, isn't it?&lt;/p&gt;

&lt;p&gt;Not really. An adequately distributed module needs to be tagged and versioned so people can trust your module for production usage.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you decide when or how to bump your package version?
&lt;/h2&gt;

&lt;p&gt;Luckily, you don't need to reinvent the wheel. Most packages inside and outside the golang community follow &lt;a href="https://semver.org/" rel="noopener noreferrer"&gt;SemVer&lt;/a&gt;. TL;DR:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You have three numbers divided by a dot (&lt;code&gt;.&lt;/code&gt;) -&amp;gt; &lt;code&gt;1.2.5&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;For changes that break the existing API, you change the first number&lt;/li&gt;
&lt;li&gt;For changes that add new capabilities but don't affect existing ones, you change the second&lt;/li&gt;
&lt;li&gt;For changes that fix existing behavior because of a bug, you change the third.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There are minor trade-offs and personal tastes that might change the practical application. Still, the core concepts are as simple as that 4 points, and with that foundation, the &lt;code&gt;semantic-release&lt;/code&gt; package was created.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/semantic-release" rel="noopener noreferrer"&gt;
        semantic-release
      &lt;/a&gt; / &lt;a href="https://github.com/semantic-release/semantic-release" rel="noopener noreferrer"&gt;
        semantic-release
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      📦🚀 Fully automated version management and package publishing
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;📦🚀 semantic-release&lt;/h1&gt;
&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Fully automated version management and package publishing&lt;/h3&gt;
&lt;/div&gt;
&lt;p&gt;
  &lt;a href="https://github.com/semantic-release/semantic-release/discussions" rel="noopener noreferrer"&gt;
    &lt;img alt="Join the community on GitHub Discussions" src="https://camo.githubusercontent.com/46e01bcc88f1f0353f8c7b325a16e398a63ba326edabd53b17621c4358b1f06b/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4a6f696e253230746865253230636f6d6d756e6974792d6f6e25323047697448756225323044697363757373696f6e732d626c7565"&gt;
  &lt;/a&gt;
  &lt;a href="https://github.com/semantic-release/semantic-release/actions/workflows/test.yml" rel="noopener noreferrer"&gt;
    &lt;img alt="Build states" src="https://github.com/semantic-release/semantic-release/actions/workflows/test.yml/badge.svg"&gt;
  &lt;/a&gt;
  &lt;a href="https://securityscorecards.dev/viewer/?uri=github.com/semantic-release/semantic-release" rel="nofollow noopener noreferrer"&gt;
    &lt;img alt="OpenSSF Scorecard" src="https://camo.githubusercontent.com/f4b9f841246ac358bf1730ad14931c3e1ca8e60d586e769230cc582610b75ccc/68747470733a2f2f6170692e736563757269747973636f726563617264732e6465762f70726f6a656374732f6769746875622e636f6d2f73656d616e7469632d72656c656173652f73656d616e7469632d72656c656173652f6261646765"&gt;
  &lt;/a&gt;
  &lt;a href="https://github.com/semantic-release/semantic-release#badge" rel="noopener noreferrer"&gt;
    &lt;img alt="semantic-release: angular" src="https://camo.githubusercontent.com/3eff76c28940aef846cd55ae47d32961338e0c0d5398fd14a1234458aa6b3a9c/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f73656d616e7469632d2d72656c656173652d616e67756c61722d6531303037393f6c6f676f3d73656d616e7469632d72656c65617365"&gt;
  &lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
  &lt;a href="https://www.npmjs.com/package/semantic-release" rel="nofollow noopener noreferrer"&gt;
    &lt;img alt="npm latest version" src="https://camo.githubusercontent.com/57ba797d1739ce87777684ae60a0f61d5acf1062feb859034910a0c2fc54583e/68747470733a2f2f696d672e736869656c64732e696f2f6e706d2f762f73656d616e7469632d72656c656173652f6c61746573742e737667"&gt;
  &lt;/a&gt;
  &lt;a href="https://www.npmjs.com/package/semantic-release" rel="nofollow noopener noreferrer"&gt;
    &lt;img alt="npm next version" src="https://camo.githubusercontent.com/8afffd7d355124865b065fd61edb8499d13d16d62f8c2ca3ba2956445f6ec781/68747470733a2f2f696d672e736869656c64732e696f2f6e706d2f762f73656d616e7469632d72656c656173652f6e6578742e737667"&gt;
  &lt;/a&gt;
  &lt;a href="https://www.npmjs.com/package/semantic-release" rel="nofollow noopener noreferrer"&gt;
    &lt;img alt="npm beta version" src="https://camo.githubusercontent.com/01050af7667f92d9c6654d3dc545f525680573e6e7d752d53341badd4a96fabf/68747470733a2f2f696d672e736869656c64732e696f2f6e706d2f762f73656d616e7469632d72656c656173652f626574612e737667"&gt;
  &lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;semantic-release&lt;/strong&gt; automates the whole package release workflow including: determining the next version number, generating the release notes, and publishing the package.&lt;/p&gt;

&lt;p&gt;This removes the immediate connection between human emotions and version numbers, strictly following the &lt;a href="http://semver.org" rel="nofollow noopener noreferrer"&gt;Semantic Versioning&lt;/a&gt; specification and communicating the &lt;strong&gt;impact&lt;/strong&gt; of changes to consumers.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Trust us, this will change your workflow for the better. – &lt;a href="https://egghead.io/lessons/javascript-how-to-write-a-javascript-library-automating-releases-with-semantic-release" rel="nofollow noopener noreferrer"&gt;egghead.io&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Highlights&lt;/h2&gt;
&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;Fully automated release&lt;/li&gt;
&lt;li&gt;Enforce &lt;a href="https://semver.org" rel="nofollow noopener noreferrer"&gt;Semantic Versioning&lt;/a&gt; specification&lt;/li&gt;
&lt;li&gt;New features and fixes are immediately available to users&lt;/li&gt;
&lt;li&gt;Notify maintainers and users of new releases&lt;/li&gt;
&lt;li&gt;Use formalized commit message convention to document changes in the codebase&lt;/li&gt;
&lt;li&gt;Publish on different distribution channels (such as &lt;a href="https://docs.npmjs.com/cli/dist-tag" rel="nofollow noopener noreferrer"&gt;npm dist-tags&lt;/a&gt;) based on git merges&lt;/li&gt;
&lt;li&gt;Integrate with your &lt;a href="https://github.com/semantic-release/semantic-releasedocs/recipes/release-workflow/README.md#ci-configurations" rel="noopener noreferrer"&gt;continuous integration workflow&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Avoid potential errors associated with manual releases&lt;/li&gt;
&lt;li&gt;Support any &lt;a href="https://github.com/semantic-release/semantic-releasedocs/recipes/release-workflow/README.md#package-managers-and-languages" rel="noopener noreferrer"&gt;package managers and languages&lt;/a&gt; via &lt;a href="https://github.com/semantic-release/semantic-releasedocs/usage/plugins.md" rel="noopener noreferrer"&gt;plugins&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Simple and reusable configuration via &lt;a href="https://github.com/semantic-release/semantic-releasedocs/usage/shareable-configurations.md" rel="noopener noreferrer"&gt;shareable configurations&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Support for &lt;a href="https://github.com/semantic-release/npm#npm-provenance" rel="noopener noreferrer"&gt;npm package provenance&lt;/a&gt; that…&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/semantic-release/semantic-release" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;h2&gt;
  
  
  Commit Convention
&lt;/h2&gt;

&lt;p&gt;Using a proper commit convention is crucial for versioning and keeping an up-to-date change log easily. I have been using &lt;a href="https://www.conventionalcommits.org/en/v1.0.0/#summary" rel="noopener noreferrer"&gt;Convetional Commit&lt;/a&gt; for a few years. From my point of view, it is the easiest to adopt and manage with a great return on investment.&lt;/p&gt;

&lt;p&gt;Check, how clean and clear the &lt;a href="https://github.com/semantic-release/semantic-release/commits/master" rel="noopener noreferrer"&gt;semantic-release git history&lt;/a&gt; feels.&lt;/p&gt;
&lt;h2&gt;
  
  
  Tag and release
&lt;/h2&gt;

&lt;p&gt;Suppose you are using &lt;a href="https://www.conventionalcommits.org/en/v1.0.0/#summary" rel="noopener noreferrer"&gt;Convetional Commit&lt;/a&gt;, and you want proper semantic versioning. In that case, the best choice is the semantic release package.&lt;/p&gt;

&lt;p&gt;At first sight, you might find it strange because the tool is written in JS, designed for the nodejs environment. Still, you can abstract those implementation details using GitHub and GitHub action.&lt;/p&gt;
&lt;h1&gt;
  
  
  Semantic Version with golang
&lt;/h1&gt;

&lt;p&gt;The package will create a release and a tag on the GitHub repository. You can check an example on my &lt;a href="https://github.com/jdvr/go-again/blob/main/.github/workflows/release.yaml" rel="noopener noreferrer"&gt;go-again repository&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You need both the GitHub Action configuration and the &lt;code&gt;.releaserc&lt;/code&gt; file.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Release&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;main&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;release&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@master&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Release&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;codfish/semantic-release-action@v2.0.0&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;



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

&lt;/div&gt;
&lt;p&gt;And the &lt;a href="https://github.com/jdvr/go-again/blob/main/.releaserc" rel="noopener noreferrer"&gt;.releaserc&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;main"&lt;/span&gt;

&lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;@semantic-release/commit-analyzer"&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;@semantic-release/release-notes-generator"&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;@semantic-release/GitHub"&lt;/span&gt;



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

&lt;/div&gt;
&lt;p&gt;Every time you merge or push a commit to the repository, you will get a new version following the conventional commit to semantic versioning:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;fix: --&amp;gt; patch&lt;/li&gt;
&lt;li&gt;feat --&amp;gt; minor&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;BREAKING CHANGES&lt;/code&gt; -&amp;gt; Major&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The translation for commit &lt;code&gt;type&lt;/code&gt; to semantic versioning bump is configurable using the &lt;a href="https://www.npmjs.com/package/@semantic-release/commit-analyzer" rel="noopener noreferrer"&gt;commit-analyzer package&lt;/a&gt;&lt;/p&gt;

</description>
      <category>go</category>
      <category>tutorial</category>
      <category>beginners</category>
    </item>
    <item>
      <title>A golang library to retry operations with exponential backoff</title>
      <dc:creator>Juan Vega</dc:creator>
      <pubDate>Fri, 30 Sep 2022 09:30:02 +0000</pubDate>
      <link>https://forem.com/juanvegadev/a-golang-library-to-retry-operations-with-exponential-backoff-8m8</link>
      <guid>https://forem.com/juanvegadev/a-golang-library-to-retry-operations-with-exponential-backoff-8m8</guid>
      <description>&lt;p&gt;I have created &lt;code&gt;go-again&lt;/code&gt;,  a small library to retry operations using different timing algorithms. By default, it uses exponential backoff with a jitter to generate various delays for each retry.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/jdvr" rel="noopener noreferrer"&gt;
        jdvr
      &lt;/a&gt; / &lt;a href="https://github.com/jdvr/go-again" rel="noopener noreferrer"&gt;
        go-again
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A set of utility algorithms to retry operations, again and again.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Go Again&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;A simple and configurable retry library for go, with exponential backoff, and constant delay support out of the box
Inspired by &lt;a href="https://github.com/cenkalti/backoff" rel="noopener noreferrer"&gt;backoff&lt;/a&gt;.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Features&lt;/h2&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Configurable delay calculation algorithm&lt;/li&gt;
&lt;li&gt;Support for exponential backoff and constant delay out of the box&lt;/li&gt;
&lt;li&gt;Support for generics&lt;/li&gt;
&lt;li&gt;Simple and clean interface&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;There are two main concepts:&lt;/h3&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Retry: Given an operation and a ticks calculator keeps retrying until either permanent error or timeout happen&lt;/li&gt;
&lt;li&gt;TicksCalculator: Provide delay for retryer to wait between retries&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Examples:&lt;/h2&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Call an API using exponential backoff&lt;/h3&gt;

&lt;/div&gt;
&lt;div class="highlight highlight-source-go notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;package&lt;/span&gt; main
&lt;span class="pl-k"&gt;import&lt;/span&gt; (
    &lt;span class="pl-s"&gt;"context"&lt;/span&gt;
    &lt;span class="pl-s"&gt;"errors"&lt;/span&gt;
    &lt;span class="pl-s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="pl-s"&gt;"net/http"&lt;/span&gt;

    &lt;span class="pl-s"&gt;"github.com/jdvr/go-again"&lt;/span&gt;
)


&lt;span class="pl-k"&gt;func&lt;/span&gt; &lt;span class="pl-en"&gt;main&lt;/span&gt;() {
    &lt;span class="pl-s1"&gt;ctx&lt;/span&gt;, &lt;span class="pl-s1"&gt;cancel&lt;/span&gt; &lt;span class="pl-c1"&gt;:=&lt;/span&gt; &lt;span class="pl-s1"&gt;context&lt;/span&gt;.&lt;span class="pl-en"&gt;WithCancel&lt;/span&gt;(&lt;span class="pl-s1"&gt;context&lt;/span&gt;.&lt;span class="pl-en"&gt;Background&lt;/span&gt;())
    &lt;span class="pl-k"&gt;defer&lt;/span&gt; &lt;span class="pl-en"&gt;cancel&lt;/span&gt;()

    &lt;span class="pl-s1"&gt;apiResponse&lt;/span&gt;, &lt;span class="pl-s1"&gt;err&lt;/span&gt; &lt;span class="pl-c1"&gt;:=&lt;/span&gt; &lt;span class="pl-s1"&gt;again&lt;/span&gt;.&lt;span class="pl-en"&gt;Retry&lt;/span&gt;[&lt;span class="pl-c1"&gt;*&lt;/span&gt;http.&lt;span class="pl-smi"&gt;Response&lt;/span&gt;](&lt;span class="pl-s1"&gt;ctx&lt;/span&gt;, &lt;span class="pl-k"&gt;func&lt;/span&gt;(&lt;span class="pl-s1"&gt;ctx&lt;/span&gt; context.&lt;span class="pl-smi"&gt;Context&lt;/span&gt;) (&lt;span class="pl-c1"&gt;*&lt;/span&gt;http.&lt;span class="pl-smi"&gt;Response&lt;/span&gt;, &lt;span class="pl-smi"&gt;error&lt;/span&gt;) {
        &lt;span class="pl-s1"&gt;fmt&lt;/span&gt;.&lt;span class="pl-en"&gt;Println&lt;/span&gt;(&lt;span class="pl-s"&gt;"Running Operation"&lt;/span&gt;)

        &lt;/pre&gt;…
&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/jdvr/go-again" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Retry any function using exponential backoff:&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;

&lt;p&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;br&gt;
    &lt;span class="s"&gt;"context"&lt;/span&gt;&lt;br&gt;
    &lt;span class="s"&gt;"errors"&lt;/span&gt;&lt;br&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;&lt;br&gt;
    &lt;span class="s"&gt;"net/http"&lt;/span&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;span class="s"&amp;gt;"github.com/jdvr/go-again"&amp;lt;/span&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;br&gt;
    &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cancel&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithCancel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Background&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;br&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;cancel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;span class="n"&amp;gt;apiResponse&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span class="n"&amp;gt;err&amp;lt;/span&amp;gt; &amp;lt;span class="o"&amp;gt;:=&amp;lt;/span&amp;gt; &amp;lt;span class="n"&amp;gt;again&amp;lt;/span&amp;gt;&amp;lt;span class="o"&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span class="n"&amp;gt;Retry&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;[&amp;lt;/span&amp;gt;&amp;lt;span class="o"&amp;gt;*&amp;lt;/span&amp;gt;&amp;lt;span class="n"&amp;gt;http&amp;lt;/span&amp;gt;&amp;lt;span class="o"&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span class="n"&amp;gt;Response&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;](&amp;lt;/span&amp;gt;&amp;lt;span class="n"&amp;gt;ctx&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span class="k"&amp;gt;func&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span class="n"&amp;gt;ctx&amp;lt;/span&amp;gt; &amp;lt;span class="n"&amp;gt;context&amp;lt;/span&amp;gt;&amp;lt;span class="o"&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span class="n"&amp;gt;Context&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;)&amp;lt;/span&amp;gt; &amp;lt;span class="p"&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span class="o"&amp;gt;*&amp;lt;/span&amp;gt;&amp;lt;span class="n"&amp;gt;http&amp;lt;/span&amp;gt;&amp;lt;span class="o"&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span class="n"&amp;gt;Response&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span class="kt"&amp;gt;error&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;)&amp;lt;/span&amp;gt; &amp;lt;span class="p"&amp;gt;{&amp;lt;/span&amp;gt;
    &amp;lt;span class="n"&amp;gt;fmt&amp;lt;/span&amp;gt;&amp;lt;span class="o"&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span class="n"&amp;gt;Println&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span class="s"&amp;gt;"Running Operation"&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;)&amp;lt;/span&amp;gt;

    &amp;lt;span class="n"&amp;gt;resp&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span class="n"&amp;gt;err&amp;lt;/span&amp;gt; &amp;lt;span class="o"&amp;gt;:=&amp;lt;/span&amp;gt; &amp;lt;span class="n"&amp;gt;http&amp;lt;/span&amp;gt;&amp;lt;span class="o"&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span class="n"&amp;gt;DefaultClient&amp;lt;/span&amp;gt;&amp;lt;span class="o"&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span class="n"&amp;gt;Get&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span class="s"&amp;gt;"https://sameflaky.api/path"&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;)&amp;lt;/span&amp;gt;
    &amp;lt;span class="k"&amp;gt;if&amp;lt;/span&amp;gt; &amp;lt;span class="n"&amp;gt;err&amp;lt;/span&amp;gt; &amp;lt;span class="o"&amp;gt;!=&amp;lt;/span&amp;gt; &amp;lt;span class="no"&amp;gt;nil&amp;lt;/span&amp;gt; &amp;lt;span class="p"&amp;gt;{&amp;lt;/span&amp;gt;
        &amp;lt;span class="c"&amp;gt;// operation will be retried&amp;lt;/span&amp;gt;
        &amp;lt;span class="k"&amp;gt;return&amp;lt;/span&amp;gt; &amp;lt;span class="no"&amp;gt;nil&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span class="n"&amp;gt;err&amp;lt;/span&amp;gt;
    &amp;lt;span class="p"&amp;gt;}&amp;lt;/span&amp;gt;

    &amp;lt;span class="k"&amp;gt;if&amp;lt;/span&amp;gt; &amp;lt;span class="n"&amp;gt;resp&amp;lt;/span&amp;gt;&amp;lt;span class="o"&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span class="n"&amp;gt;StatusCode&amp;lt;/span&amp;gt; &amp;lt;span class="o"&amp;gt;==&amp;lt;/span&amp;gt; &amp;lt;span class="n"&amp;gt;http&amp;lt;/span&amp;gt;&amp;lt;span class="o"&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span class="n"&amp;gt;StatusForbidden&amp;lt;/span&amp;gt; &amp;lt;span class="p"&amp;gt;{&amp;lt;/span&amp;gt;
        &amp;lt;span class="c"&amp;gt;// no more retries&amp;lt;/span&amp;gt;
        &amp;lt;span class="k"&amp;gt;return&amp;lt;/span&amp;gt; &amp;lt;span class="no"&amp;gt;nil&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span class="n"&amp;gt;again&amp;lt;/span&amp;gt;&amp;lt;span class="o"&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span class="n"&amp;gt;NewPermanentError&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span class="n"&amp;gt;errors&amp;lt;/span&amp;gt;&amp;lt;span class="o"&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span class="n"&amp;gt;New&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span class="s"&amp;gt;"no retry, permanent error"&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;))&amp;lt;/span&amp;gt;
    &amp;lt;span class="p"&amp;gt;}&amp;lt;/span&amp;gt;

    &amp;lt;span class="k"&amp;gt;if&amp;lt;/span&amp;gt; &amp;lt;span class="n"&amp;gt;resp&amp;lt;/span&amp;gt;&amp;lt;span class="o"&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span class="n"&amp;gt;StatusCode&amp;lt;/span&amp;gt; &amp;lt;span class="o"&amp;gt;&amp;amp;gt;&amp;lt;/span&amp;gt; &amp;lt;span class="m"&amp;gt;400&amp;lt;/span&amp;gt; &amp;lt;span class="p"&amp;gt;{&amp;lt;/span&amp;gt;
        &amp;lt;span class="k"&amp;gt;return&amp;lt;/span&amp;gt; &amp;lt;span class="no"&amp;gt;nil&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span class="n"&amp;gt;errors&amp;lt;/span&amp;gt;&amp;lt;span class="o"&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span class="n"&amp;gt;New&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span class="s"&amp;gt;"this will be retry"&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;)&amp;lt;/span&amp;gt;
    &amp;lt;span class="p"&amp;gt;}&amp;lt;/span&amp;gt;

    &amp;lt;span class="c"&amp;gt;// do whatever you need with a valid response ...&amp;lt;/span&amp;gt;

    &amp;lt;span class="k"&amp;gt;return&amp;lt;/span&amp;gt; &amp;lt;span class="n"&amp;gt;resp&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span class="no"&amp;gt;nil&amp;lt;/span&amp;gt; &amp;lt;span class="c"&amp;gt;// no retry&amp;lt;/span&amp;gt;
&amp;lt;span class="p"&amp;gt;})&amp;lt;/span&amp;gt;
&amp;lt;span class="k"&amp;gt;if&amp;lt;/span&amp;gt; &amp;lt;span class="n"&amp;gt;err&amp;lt;/span&amp;gt; &amp;lt;span class="o"&amp;gt;!=&amp;lt;/span&amp;gt; &amp;lt;span class="no"&amp;gt;nil&amp;lt;/span&amp;gt; &amp;lt;span class="p"&amp;gt;{&amp;lt;/span&amp;gt;
    &amp;lt;span class="nb"&amp;gt;panic&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span class="n"&amp;gt;err&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;)&amp;lt;/span&amp;gt;
&amp;lt;span class="p"&amp;gt;}&amp;lt;/span&amp;gt;

&amp;lt;span class="n"&amp;gt;fmt&amp;lt;/span&amp;gt;&amp;lt;span class="o"&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span class="n"&amp;gt;Printf&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span class="s"&amp;gt;"Finished with response %v&amp;lt;/span&amp;gt;&amp;lt;span class="se"&amp;gt;\n&amp;lt;/span&amp;gt;&amp;lt;span class="s"&amp;gt;"&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span class="n"&amp;gt;apiResponse&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;)&amp;lt;/span&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/p&gt;

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

&lt;/div&gt;
&lt;h1&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Exponential backoff and jitter&lt;br&gt;
&lt;/h1&gt;

&lt;p&gt;Quoting &lt;a href="https://en.wikipedia.org/wiki/Exponential_backoff" rel="noopener noreferrer"&gt;Wikpedia&lt;/a&gt; about Exponential Backoff:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Exponential backoff is an algorithm that uses feedback to multiplicatively decrease the rate of some process to gradually find an acceptable rate.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The idea is to generate longer wait periods between each retry, assuming the system will work at some point because the issue is just a matter of time.&lt;/p&gt;

&lt;p&gt;Suppose you have an API to get the user profile that might fail. In that case, you can introduce a retry function to keep trying the request, and this retry will wait longer between each period. Now imagine that your API crashed due to workload.&lt;/p&gt;

&lt;p&gt;Backoff won't help. Although the request will be triggered with a delay period, all the clients follow the same algorithm with the same delays, so your workload issue is even worse. &lt;/p&gt;

&lt;p&gt;The algorithm uses a jitter to solve the issue of the exact delays by generating a small random delay gap between different clients. Instead of having all clients waiting for 500ms, they pick a random number between 450 and 550, distributing the server workload wisely.&lt;/p&gt;

&lt;h1&gt;
  
  
  Go Again
&lt;/h1&gt;

&lt;p&gt;I created this library for fun, but it is production ready, and of course, I will use it as soon as I have the opportunity. It is inspired by &lt;a href="https://github.com/cenkalti/backoff" rel="noopener noreferrer"&gt;backoff&lt;/a&gt;, the library I am using now.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;go-again&lt;/code&gt; goes beyond exponential backoff and offers another algorithm for &lt;a href="https://github.com/jdvr/go-again/#call-database-keeping-a-constant-delay" rel="noopener noreferrer"&gt;a constant delay&lt;/a&gt; in case you want to keep it simple. Furthermore, you can define &lt;a href="https://github.com/jdvr/go-again/blob/main/again.go#L30" rel="noopener noreferrer"&gt;your own algorithm easily by implementing an interface&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>go</category>
      <category>programming</category>
    </item>
    <item>
      <title>Primeros pasos del testing en un proyecto Go</title>
      <dc:creator>Juan Vega</dc:creator>
      <pubDate>Fri, 23 Sep 2022 17:28:36 +0000</pubDate>
      <link>https://forem.com/juanvegadev/primeros-pasos-del-testing-en-un-proyecto-go-56bn</link>
      <guid>https://forem.com/juanvegadev/primeros-pasos-del-testing-en-un-proyecto-go-56bn</guid>
      <description>&lt;p&gt;This article is my opinion and it follows the &lt;em&gt;&lt;a href="https://justsharing.dev/"&gt;just sharing&lt;/a&gt; principle&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Aunque go trae un runner de test en su toolchain,&lt;code&gt;go test&lt;/code&gt; que funciona efectivamente bien. Las necesidades de un proyecto mediano o grande van más allá de hacer simplemente &lt;code&gt;go test ./...&lt;/code&gt; y que lo ejecute todo. Si los test no son cómodos de usar, lo más probable es que se acaben dejando de lado y se ejecuten solamente en el servidor de CI haciendo los desarrollos cada vez más lentos.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table Of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;La pirámide&lt;/li&gt;
&lt;li&gt;El Sujeto&lt;/li&gt;
&lt;li&gt;Posibles soluciones&lt;/li&gt;
&lt;li&gt;Allanando el camino&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  La pirámide &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Aunque no voy a usar un naming estricto, para intentar guiar los ejemplos voy a hablar de diferentes tipologías de test, para entender porque es importante tenerlos separados hay que tener un poco en mente &lt;a href="https://martinfowler.com/bliki/TestPyramid.html"&gt;la pirámide de test&lt;/a&gt;. Cada &lt;em&gt;capa&lt;/em&gt; tiene un objetivo, un ciclo de feedback y validan diferentes partes del sistema.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--x8QT6X6I--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b1raxwwgo31yge6i5zbo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--x8QT6X6I--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b1raxwwgo31yge6i5zbo.png" alt="pirámide de test, unit abajo, acceptance arriba" width="560" height="300"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Normalmente en otros lenguajes que he trabajado, suele haber (al menos) 3 grupos que se repiten siempre y que las diferentes herramientas soportan a aunque no siempre con el mismo nombre:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Acceptance (end to end): Test que van desde el punto más externo y expuesto del sistema hasta la parte más interna, ejemplo, Llamar al API expuesta para crear una entidad en la BBDD.&lt;/li&gt;
&lt;li&gt;Integration: Testamos como algunas partes de nuestro sistema interactúan con elementos externos desde el punto de vista del sujeto del test. En ocasiones hay que incluso define integración dentro de un mismo proyecto, no voy a entrar en ese debate ahora mismo. Un ejemplo sería hacer un test del repositorio usando una base de datos en docker.&lt;/li&gt;
&lt;li&gt;Unitarios: Igualmente no voy a entrar a definir que es la unidad, pero son los test que mejor aislados están desde el punto de vista del sujeto. Un ejemplo es tener un test que pruebe el algoritmo que usamos para calcular la puntuación de un partido.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Lo importante, más allá de definiciones para este post es tener claro que la jerarquía aquí la uso para definir que &lt;strong&gt;cada grupo tiene un ciclo de feedback más corto que el anterior&lt;/strong&gt;, por ejemplo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Acceptace: 10s&lt;/li&gt;
&lt;li&gt;Integración: 3s&lt;/li&gt;
&lt;li&gt;Unitarios: &amp;lt;100ms&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Con esta premisa, la idea es tener una configuración que nos permita hacer uso de los diferentes niveles de información de forma cómoda y sin conflicto.&lt;/p&gt;

&lt;h2&gt;
  
  
  El sujeto &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;En los ejemplos me basaré en una función muy básica que simula una función para calcular el precio de un producto en función del stock que queda. He ignorado toda la gestión de errores por simplicidad.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;calculator&lt;/span&gt; &lt;span class="n"&gt;PriceCalculator&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Calculate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt; &lt;span class="n"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;float64&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;stock&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;calculator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stockChecker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;gap&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;stock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Gap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BaseRate&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;gap&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Voy a tener tres suite de test diferentes, una donde uso un doble de prueba para el &lt;code&gt;stockChecker&lt;/code&gt;, otra donde incluyo una implementación real que se conecta a la base de datos para &lt;code&gt;stockChecker&lt;/code&gt;, y por ultima uno donde simulo llamar a un API HTTP que usa el &lt;code&gt;PriceCalculator&lt;/code&gt; por debajo. El detalle de como escribo cada test lo dejo para otro post. En resumen tendríamos tres test:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;TestPriceCalculator_Calculate&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TestPriceCalculatorIntegration&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TestPriceCalculatorAPI&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tenemos que asumir lo que comenté más arriba. Cada test tarda al menos el doble o el tripe que el anterior, siendo el de más arriba el más rápido. Tenemos que buscar la forma de dividir los ciclos de feedback para mejorar la Developer Experience.&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/adyen" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__org__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VPpRpVHD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--9pslhaCj--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_150%2Cq_auto%2Cw_150/https://dev-to-uploads.s3.amazonaws.com/uploads/organization/profile_image/2288/2bc231f0-951a-4eeb-8dfa-0ba1ce108adc.jpg" alt="Adyen" width="150" height="150"&gt;
      &lt;div class="ltag__link__user__pic"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--59fbB5m1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--my6q8h1f--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_150%2Cq_auto%2Cw_150/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/178939/38a82d99-3a0c-4a47-84fc-76fff3144cda.png" alt="" width="150" height="150"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/adyen/what-is-developer-experience-and-why-should-we-care-1k9i" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;What is Developer Experience and why should we care?&lt;/h2&gt;
      &lt;h3&gt;Deepu K Sasidharan for Adyen ・ Jul 16 '21 ・ 6 min read&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#devrel&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#engineering&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#discuss&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;



&lt;h2&gt;
  
  
  Posibles soluciones &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Convención de nombres
&lt;/h3&gt;

&lt;p&gt;Una de las primeras opciones que me vino a la mente fue llegar a un consenso sobre el naming de los test. Hay soporte para esto en &lt;code&gt;go test&lt;/code&gt; y lo uso habitualmente:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;-run&lt;/span&gt; &lt;span class="s2"&gt;"regexp"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La opción -run evalúa todo los test del proyecto y ejecuta solo los que coinciden con la expresión definida.&lt;/p&gt;

&lt;p&gt;Esta solución es valida y como digo la uso habitualmente cuando quiero ejecutar solo un test (o un grupo) de toda la suite y estoy usando el terminal. El problema principal es que no hay prácticamente costumbre de esto en la comunidad ni en mi empresa actual e iba a ser algo que explicar a cada nueva persona que se uniese.&lt;/p&gt;

&lt;p&gt;Incluso por error se podían acabar mezclando suites, un simple typo en la palabra &lt;code&gt;Integration&lt;/code&gt; y podría hacer que un test se fuese a otro &lt;em&gt;grupo&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build tags
&lt;/h2&gt;

&lt;p&gt;Si pones un comentario al inicio de fichero usando la palabra &lt;code&gt;+build&lt;/code&gt; lo que haces es declarar un build tag que indican al copilador y al runner de test que ese fichero pertenece a un grupo concreto.&lt;/p&gt;

&lt;p&gt;En nuestro caso sería poner al principio de cada fichero de test algo parecido a&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;//go:build integration&lt;/span&gt;
&lt;span class="o"&gt;---&lt;/span&gt;&lt;span class="n"&gt;Ambas&lt;/span&gt; &lt;span class="n"&gt;notaciones&lt;/span&gt; &lt;span class="s"&gt;`+build`&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="s"&gt;`go:build`&lt;/span&gt; &lt;span class="n"&gt;son&lt;/span&gt; &lt;span class="n"&gt;validas&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="c"&gt;//+build acceptance&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esta opción tiene algunos adeptos en la comunidad y se pueden encontrar post explicando como hay gente que los usa. El principal handicap para mi es que hay que tener bien configurado el IDE o puede resultar que tengas un test que no compila pero no te avisa porque esta fuera de las tags que esta mirando el IDE. Además, al igual que en la opción anterior, es sencillo dejar fuera un test por error o acabar con &lt;em&gt;zombi&lt;/em&gt; code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cortos o largos, la solución de &lt;code&gt;go test&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;La solución más común y la que finalmente he acabado adoptando por simplicidad a largo plazo. Valoro mucho tener algo que &lt;em&gt;cualquier desarrollador Go puede heredar sin problemas.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Si no lo conoces entre los flags disponibles de &lt;code&gt;go test&lt;/code&gt; tenemos &lt;code&gt;-shot&lt;/code&gt;, ¿Qué hace &lt;code&gt;-short&lt;/code&gt; veamoslo con un ejemplo?:&lt;/p&gt;

&lt;p&gt;Si tenemos un test de integración:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;TestPriceCalculatorIntegration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;testing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// setup&lt;/span&gt;
    &lt;span class="n"&gt;calculator&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;PriceCalculator&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;StockChecker&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;PostgreSQLStockChecker&lt;/span&gt;&lt;span class="p"&gt;{}}&lt;/span&gt;
    &lt;span class="c"&gt;// setup&lt;/span&gt;

    &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;calculator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Calculate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"ASLSDFK000123"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Tanto el setup como la ejecución del mismo puede ser lenta así que manualmente lo marcamos:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;TestPriceCalculatorIntegration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;testing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;testing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Short&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Skip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// setup&lt;/span&gt;
    &lt;span class="n"&gt;calculator&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;PriceCalculator&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;StockChecker&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;PostgreSQLStockChecker&lt;/span&gt;&lt;span class="p"&gt;{}}&lt;/span&gt;
    &lt;span class="c"&gt;// setup&lt;/span&gt;

    &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;calculator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Calculate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"ASLSDFK000123"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;testing.Short()&lt;/code&gt; nos permite saber si el flag &lt;code&gt;-short&lt;/code&gt; ha sido incluido en el comando &lt;code&gt;go test&lt;/code&gt;, de forma que para el siguiente comando sería &lt;code&gt;true&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;-race&lt;/span&gt; &lt;span class="nt"&gt;-short&lt;/span&gt; ./...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;De esta forma podemos generar dos ciclos de feedback, evidentemente esta opción tiene problemas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Solo tienes &lt;em&gt;dos niveles&lt;/em&gt; de test.&lt;/li&gt;
&lt;li&gt;Alguien puede equivocarse al marcar un &lt;code&gt;short&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Por defecto, los test &lt;code&gt;-short&lt;/code&gt; también se ejecutan al no incluir &lt;code&gt;-short&lt;/code&gt; porque solo marcamos los lentos.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Aunque como menciono lo más relevante es que esta opción es la más común y la más conocida así que me parece la más eficaz.&lt;/p&gt;

&lt;h2&gt;
  
  
  Allanando el camino &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Como hemos visto al final que test se ejecutan depende principalmente de los flag que hay en el comando &lt;code&gt;go test&lt;/code&gt;, para facilitar esto normalmente utilizo &lt;code&gt;Makefile&lt;/code&gt; de forma que simplemente con &lt;code&gt;make test-unit&lt;/code&gt; o &lt;code&gt;make test-integration&lt;/code&gt; se ejecuta el comando adecuado y el desarrollador no necesita recordar los flags.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.pexels.com/photo/person-wearing-white-and-black-mid-rise-sneakers-at-borobudur-indonesia-346834/"&gt;Photo by Porapak Apichodilok from Pexels&lt;/a&gt;&lt;/p&gt;

</description>
      <category>go</category>
      <category>testing</category>
      <category>architecture</category>
      <category>spanish</category>
    </item>
    <item>
      <title>Language and framework agnostic database migrations.</title>
      <dc:creator>Juan Vega</dc:creator>
      <pubDate>Tue, 13 Sep 2022 12:16:12 +0000</pubDate>
      <link>https://forem.com/juanvegadev/language-and-framework-agnostic-database-migrations-56bj</link>
      <guid>https://forem.com/juanvegadev/language-and-framework-agnostic-database-migrations-56bj</guid>
      <description>&lt;p&gt;It doesn't matter how simple your app is. If it needs a database, you will probably need a database (SQL) schema migration manager. I know other languages and frameworks bring schema management with the ORM, but what happens if you want to use plain SQL? I usually don't work with ORM.&lt;/p&gt;

&lt;p&gt;We use &lt;a href="https://github.com/golang-migrate/migrate"&gt;Migrate&lt;/a&gt; because Go is our default language in Typeform. The following tutorial could work for any language or framework because the CLI works with SQL files.&lt;/p&gt;

&lt;h2&gt;
  
  
  The tool: Migrate
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/golang-migrate/migrate"&gt;Migrate&lt;/a&gt; is a CLI and Golang library to manage migrations. One key feature is that migrations are plain SQL files.&lt;/p&gt;

&lt;p&gt;The defined principles of the library follow the &lt;a href="https://people.apache.org/~fhanik/kiss.html"&gt;KISS&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Migrate reads migrations from sources and applies them in the correct order to a database.&lt;/li&gt;
&lt;li&gt;Drivers are "dumb" migrate glues everything together and makes sure the logic is bulletproof. (Keeps the drivers lightweight, too.)&lt;/li&gt;
&lt;li&gt;Database drivers don't assume things or try to correct user input. When in doubt, fail.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As it stands by the quoted principles, we will have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A folder with SQL files.&lt;/li&gt;
&lt;li&gt;Each migration is composed of two files: &lt;code&gt;up&lt;/code&gt; and &lt;code&gt;down&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Migrate CLI (&lt;code&gt;migrate&lt;/code&gt;), among others, have two main commands: &lt;code&gt;up&lt;/code&gt; and &lt;code&gt;down&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Setup: Your first migration
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;migrate&lt;/code&gt; the CLI tool brings a &lt;code&gt;create&lt;/code&gt; command that will generate migrations files following a sequence of numbers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run -v $PWD:/db migrate/migrate:v4.15.2 create -ext sql -dir db/migrations -seq create_users_table
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The previous command will generate two SQL files inside &lt;code&gt;db/migrations&lt;/code&gt; directory prefixed by &lt;code&gt;00001&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; ls db/migrations
000001_create_users_table.down.sql
000001_create_users_table.up.sql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can run it multiple times to see how the prefix increases:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; ls db/migrations
000002_create_users_table.down.sql
000002_create_users_table.up.sql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But if you delete existing files, it will generate &lt;code&gt;0001&lt;/code&gt; again. Of course, you won't manually delete migration files, but &lt;strong&gt;what happens if two dev are working at the same time and need a migration?&lt;/strong&gt;. They will generate conflict migrations (same sequence number). &lt;/p&gt;

&lt;p&gt;To mitigate this, although it doesn't fix it 100%, I generate migration files using epoch time as sequence number:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;touch migrations/$(shell date '+%s')_$(name).up.sql
touch migrations/$(shell date '+%s')_$(name).down.sql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You now have a &lt;code&gt;db&lt;/code&gt; folder with a &lt;code&gt;migrations&lt;/code&gt; subfolder with two SQL files for one migration. To run the migration, you need to call &lt;code&gt;migrate&lt;/code&gt;, but how do you make it reproducible for all environments?&lt;/p&gt;

&lt;h2&gt;
  
  
  Delivery migrations: Docker
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/golang-migrate/migrate#docker-usage"&gt;Migrate projects builds its docker image&lt;/a&gt;, so delivering migration is nuts, we create &lt;code&gt;db/Dockerfile&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM migrate/migrate:v4.15.2

COPY . .

ENTRYPOINT ["/run.sh"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Start from their image, fixed version.&lt;/li&gt;
&lt;li&gt;Copy all content of the &lt;code&gt;db&lt;/code&gt; folder to the image&lt;/li&gt;
&lt;li&gt;Set &lt;code&gt;run.sh&lt;/code&gt; as the default entrypoint&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The &lt;code&gt;run.sh&lt;/code&gt; is a handy script to grab DB connection data from the environment and run any migrate command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/usr/bin/env sh

set -e

# Grab DB config

DB_HOST=${DB_HOST:?}
DB_NAME=${DB_NAME:?}
DB_PASSWORD=${DB_PASSWORD:?}
DB_PORT=${DB_PORT:?}
DB_TIMEOUT=${DB_TIMEOUT:-10}
DB_USER=${DB_USER:?}

# wait for the database to be ready
for i in $(seq $DB_TIMEOUT) ; do
  echo "I am waiting for db to be read =&amp;gt; $DB_HOST:$DB_PORT"
  if ! nc -z $DB_HOST $DB_PORT &amp;gt; /dev/null 2&amp;gt;&amp;amp;1; then
    sleep 1
  else
    break
  fi
done

echo "Running migrations"

# For this example I am using PostgreSQL
DB_URL="postgresql://${DB_USER}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME}?sslmode=disable"

# Run the migrate cli include inside parent image.
migrate -verbose -path=./migrations -database=$DB_URL "$@"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's recap, inside the &lt;code&gt;db&lt;/code&gt; directory, you have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;migrations&lt;/code&gt; with two SQL files with a dummy create table&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Dockerfile&lt;/code&gt;, which defines an image starting from &lt;code&gt;migrate&lt;/code&gt; and embedding &lt;code&gt;migrations&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;run.sh&lt;/code&gt; is a handy script to wrap &lt;code&gt;migrate&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Run it: Docker Compose
&lt;/h2&gt;

&lt;p&gt;Now that there is a Dockerfile, you can create a docker image to run anywhere. I will follow up with an example for a local setup using docker-compose.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;version: '3'
services:
  database:
    image: 'postgres:14-alpine'
    ports:
      - "5432:5432"
    environment:
      POSTGRES_USER: username
      POSTGRES_PASSWORD: password
      POSTGRES_DB: localdb
    volumes:
      - database:/var/lib/postgresql/data

  migrations:
    build:
      context: .
    command:
      - "up"
    environment:
      - DB_HOST=database
      - DB_NAME=localdb
      - DB_PORT=5432
      - DB_USER=username
      - DB_PASSWORD=password
    depends_on:
      - database


volumes:
  database:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice how we set the environment for &lt;code&gt;migrations&lt;/code&gt; with DB parameters that will be read by &lt;code&gt;run.sh&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So, with all setup to run your migration, you just need to run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker-compose run migrations up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can check that the migration is applied:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; psql -U username -W -h localhost localdb
Password: 
psql (14.4, server 14.5)
Type "help" for help.

localdb=# select * from schema_migrations;
 version | dirty 
---------+-------
       1 | f
(1 row)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also test down command or any other &lt;code&gt;migrate&lt;/code&gt; command running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker-compose run migrations down 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It will apply down file and clean &lt;code&gt;schema_migrations&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recap
&lt;/h2&gt;

&lt;p&gt;Migrate is an agnostic, easy-to-use yet powerful SQL schema migrations manager tool. You can set up the full flow with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A folder with the SQL files&lt;/li&gt;
&lt;li&gt;A docker image package with SQL with a proper entry point&lt;/li&gt;
&lt;li&gt;Config to run the docker image.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can find all code in my github: &lt;a href="https://github.com/jdvr/migrate-template"&gt;https://github.com/jdvr/migrate-template&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I hope you find useful this small tutorial. How do you usually manage your migrations?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.pexels.com/photo/flock-of-birds-on-water-1646139/"&gt;Photo by vishnudeep  dixit from Pexels&lt;/a&gt;&lt;/p&gt;

</description>
      <category>database</category>
      <category>docker</category>
      <category>tutorial</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Typeform Navigator: My Raycast Extension</title>
      <dc:creator>Juan Vega</dc:creator>
      <pubDate>Mon, 05 Sep 2022 08:10:53 +0000</pubDate>
      <link>https://forem.com/juanvegadev/typeform-navigator-my-raycast-extension-32i3</link>
      <guid>https://forem.com/juanvegadev/typeform-navigator-my-raycast-extension-32i3</guid>
      <description>&lt;p&gt;I discovered &lt;a href="https://raycast.com"&gt;Raycast&lt;/a&gt; a few months ago, at first sight I notice the power of extensions. You can create automation and handy commands for everything.&lt;/p&gt;

&lt;p&gt;I am a Software Engineer at Typeform, so my day to day use of the platform is sightly different to a standard user or customer. I have a lot of workspaces, some shared, some private and within them I have a lot of testing forms, each for every use case I usually work with. So, I have created &lt;a href="https://www.raycast.com/jdvr/typeform"&gt;Typeform Navigator&lt;/a&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  Typeform Navigator
&lt;/h2&gt;

&lt;p&gt;Typeform Navigator allows you to easily check form main stats, navigate to workspace page or to a form results page. There are other handy commands like copy form filling url or form ID.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Wg0bI1oL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xnq7y7hr91ti6hwpzrmc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Wg0bI1oL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xnq7y7hr91ti6hwpzrmc.png" alt="Screenshot of form overview on Typeform Navigator" width="880" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kfHqBSwA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8l846zx1xcb7dsed7x8m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kfHqBSwA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8l846zx1xcb7dsed7x8m.png" alt="Screenshot of workspace forms listing on Typeform Navigator" width="880" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a Raycast extension
&lt;/h2&gt;

&lt;p&gt;Typeform Navigator is a nice example of raycast extension because it includes, listing, detail and it requires configuration (a personal token). You can found the source code on my github: &lt;a href="https://github.com/jdvr/typeform-navigator"&gt;jdvr/typeform-navigator&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Developer experience is a key advantage of Raycast you can check their &lt;a href="https://developers.raycast.com/"&gt;Developer Documentation&lt;/a&gt;, starting a new extension is really easy.&lt;/p&gt;

&lt;p&gt;The only pain point is publishing in on their storage because you need to create a pull request to their &lt;a href="https://github.com/raycast/extensions/pull/1261"&gt;extensions repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you are a Typeform user, test the extension and let me know what do you think.&lt;/p&gt;

&lt;p&gt;The project was part of Hive Sprint, a two weeks hackathon where Typeform employees have freedom to spend time on any initiative that might improve the platform or the product.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>showdev</category>
      <category>productivity</category>
      <category>react</category>
    </item>
    <item>
      <title>DDD Europe 2022</title>
      <dc:creator>Juan Vega</dc:creator>
      <pubDate>Sat, 30 Jul 2022 16:36:00 +0000</pubDate>
      <link>https://forem.com/juanvegadev/ddd-europe-2022-1ki5</link>
      <guid>https://forem.com/juanvegadev/ddd-europe-2022-1ki5</guid>
      <description>&lt;p&gt;23rd and 24th of July, I attended for the first time to DDD Europe. I have been listening to good words about this conference for the last 4-5 years. My fellows had attended several times, and I was excited to go with them this time.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Conference
&lt;/h2&gt;

&lt;p&gt;Quoting &lt;a href="https://twitter.com/mathiasverraes"&gt;Mathias Verraes&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;DDD EU is the biggest DDD conference, followed by the DDD Foundations sibling conference. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Furthermore, the event is not just significant because of its number of attendees but also thanks to a lot of great speakers and &lt;a href="https://www.youtube.com/channel/UC3PGn-hQdbtRiqxZK9XBGqQ"&gt;how much content they have generated during all the past editions&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Talks
&lt;/h2&gt;

&lt;p&gt;I assume that they shared all talks in the video sooner or later. I will update the post with them. One important thing from the conference is how it goes deep into all aspects of DDD and not just tactical patterns.&lt;/p&gt;

&lt;p&gt;An overview of the talks that I liked most:&lt;/p&gt;

&lt;h3&gt;
  
  
  Opening keynote: A Commune in the Ivory Tower? - A New Approach to Architecture
&lt;/h3&gt;

&lt;p&gt;The first keynote was also an experiment with 5 speakers. They tried to create an interactive session, gathering feedback from the audience in real-time and giving voice to their responses and opinions. &lt;/p&gt;

&lt;p&gt;I don't like it, but I appreciate the brave of trying something new.&lt;/p&gt;

&lt;h3&gt;
  
  
  An Introduction to Residuality Theory
&lt;/h3&gt;

&lt;p&gt;Do you know &lt;a href="https://www.gremlin.com/community/tutorials/chaos-engineering-the-history-principles-and-practice/"&gt;Chaos Engineering&lt;/a&gt;? What if it wouldn't be necessary until having the whole system in production to evaluate what happens if component C dies.&lt;/p&gt;

&lt;p&gt;Barry O'Reilly &lt;a href="https://www.sciencedirect.com/science/article/pii/S1877050920305585"&gt;has created a theory&lt;/a&gt; to build resilient software by introducing different stressors into the system.&lt;/p&gt;

&lt;p&gt;Once you have identified your stressor, you will know what is left and how it should behave to ensure the best outcome.&lt;/p&gt;

&lt;h3&gt;
  
  
  Domain-Driven Design in ProductLand
&lt;/h3&gt;

&lt;p&gt;As himself defined, Alberto Brandolini created and got into a rabbit hole by speaking about how we can effectively apply DDD to a product company. How do we prioritize things? Which parts of the canonical DDD are still valid? How do you bring all domain experts if maybe some of them are outside of the company (customers)?&lt;/p&gt;

&lt;h3&gt;
  
  
  How the right architecture helps you make big changes
&lt;/h3&gt;

&lt;p&gt;A hands-on workshop exemplifies how having a good architecture enables an easier refactor of the system when a new requirement arrives. &lt;a href="https://github.com/nkrijnen/workshop-ddd-europe-2022-06"&gt;You can find the base code with some instructions on GH&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Technical Coaching with the Samman method
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/emilybache"&gt;Emily Blanche&lt;/a&gt; is a great coach with a lot of experience and always shares her knowledge. This time he made an introduction to &lt;a href="https://sammancoaching.org/"&gt;her own book about technical coaching&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;In my experience, the community has embraced the idea of tactical patterns and parked aside all strategical patterns discussions. DDD Europe is just the opposite, there is room to talk about implementation details, but it is just a part of a whole. &lt;/p&gt;

&lt;p&gt;On the other hand, I don't see it as a friendly conference for beginners. I haven't attended, but they created DDD Foundations probably because they already know. &lt;/p&gt;

&lt;p&gt;It is a pleasure to have such a great conference here in Europe, and if you are a DDD enthusiast, I will encourage you to attend at least one. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://twitter.com/ddd_eu/status/1539881114726187008/photo/1"&gt;Photo by DDD EU Twitter Account&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ddd</category>
      <category>career</category>
      <category>news</category>
    </item>
    <item>
      <title>JBCNConf 2022: A great farewell</title>
      <dc:creator>Juan Vega</dc:creator>
      <pubDate>Sat, 23 Jul 2022 09:51:20 +0000</pubDate>
      <link>https://forem.com/juanvegadev/jbcnconf-2022-a-great-farewell-3gk7</link>
      <guid>https://forem.com/juanvegadev/jbcnconf-2022-a-great-farewell-3gk7</guid>
      <description>&lt;p&gt;During the past few days (July 18th-20th), I have attended JBCNConf, Spain's biggest Java/JVM conference.&lt;/p&gt;

&lt;h2&gt;
  
  
  About the conference
&lt;/h2&gt;

&lt;p&gt;Luckily I was able to attend thanks to the raffle of &lt;a href="https://twitter.com/MadridJUG"&gt;MadridJUG&lt;/a&gt; who gifted me a ticket, and &lt;a href="https://www.typeform.com/"&gt;Typeform&lt;/a&gt; which covered the cost. This edition was the last one around Java/JVM. Next year they will rename the conference to &lt;a href="https://twitter.com/dev_bcn"&gt;DevBcn&lt;/a&gt; to cover more topics.&lt;/p&gt;

&lt;p&gt;It was my first year, but I think they had made a grand farewell, a conference with more than one thousand attendees, and a lot of talks (~100).&lt;/p&gt;

&lt;p&gt;Although they titled the conference &lt;em&gt;"Women in tech"&lt;/em&gt;, and each room had the name of a famous woman engineer who contributed to the history of programming. I missed a bit more transparency on diversity matters. It is also my fault because I didn't ask them about the data. I went to several talks with women speakers, but I would say ~70% of speakers were men. I would highlight it as a point to improve on &lt;a href="https://twitter.com/dev_bcn"&gt;DevBcn&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Talks
&lt;/h2&gt;

&lt;p&gt;Here is the list of talks that I attended. I will not share all detailed notes but a small overview.&lt;/p&gt;

&lt;h3&gt;
  
  
  Artificial Intelligence needs Backend &amp;amp; DevOps to reach the real-world
&lt;/h3&gt;

&lt;p&gt;Nerea Luis is a great communicator and speaker. She explained why MLOps is essential and how AI models and systems have been &lt;em&gt;productized&lt;/em&gt; and need support from other roles (Backend, SRE, Security...) who already have experience shipping customer-focused software to extract the most of AI models.&lt;/p&gt;

&lt;p&gt;She made mentions to &lt;a href="https://ml-ops.org/"&gt;ML-Ops&lt;/a&gt; and &lt;a href="https://mlflow.org/"&gt;MLFlow&lt;/a&gt; including &lt;a href="https://cloud.google.com/vertex-ai"&gt;Vertex AI&lt;/a&gt; the GCP implementation. I will post the video as soon as it is available. In the meantime, you can enjoy  &lt;a href="https://nerealuis.es/charlas.html"&gt;any other talk from Nerea Luis&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The hidden gems of distributed tracing
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://twitter.com/ammbra1508"&gt;Ana-Maria Mihalceanu&lt;/a&gt; made a great talk with live coding on how to do distributed tracing using &lt;a href="https://www.jaegertracing.io/download/"&gt;jaeger&lt;/a&gt; and &lt;a href="https://opentelemetry.io/docs/concepts/signals/traces/"&gt;opentelemetry&lt;/a&gt;. She explained how open telemetry tracing works and different sampling approaches. All the code is available at GitHub: &lt;a href="https://github.com/ammbra/hidden-gems"&gt;https://github.com/ammbra/hidden-gems&lt;/a&gt;. At this talk, I also discovered &lt;a href="https://developers.redhat.com/developer-sandbox"&gt;RedHat Free Sanbox&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Secrets of Performance Tuning Java on Kubernetes
&lt;/h3&gt;

&lt;p&gt;A funny talk about the JVM heuristics to choose GC depending on available resources with a few takeaways:&lt;br&gt;
We usually trust JVM to select a GC without knowing how it works.&lt;br&gt;
Consider larger pods with higher throughput over more pods.&lt;br&gt;
Avoid HA cargo cult that sometimes forces us to have multiple pods increasing our bill and leading to worse resource usage optimization.&lt;br&gt;
&lt;a href="https://www.slideshare.net/brunoborges/secrets-of-performance-tuning-java-on-kubernetes"&gt;Check the slides with all the data and quick-wins&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Del final al principio: Un viaje guiado por métricas
&lt;/h3&gt;

&lt;p&gt;Adrian and Alberto, two engineers from Wallapop, shared their experience building the saved searches feature. They journeyed from a very complex and high-cost solution to a more practical but still valid approach covering 80% of use cases with a reduced cost. From my point of view, talks that spread Real World™️ experiences are the most valuable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Libera la potencia de tus aplicaciones con Micronaut y GraalVM
&lt;/h3&gt;

&lt;p&gt;I worked with &lt;a href="https://micronaut.io/"&gt;Micronaut&lt;/a&gt; back in 2019-2020, and I liked it but putting my taste aside, the work they are doing to enable full integration with &lt;a href="https://www.graalvm.org/"&gt;GraalVM&lt;/a&gt; deserves a watch. Suppose you are a developer using Spring or Quarkus and haven't tested Micronaut. Give it a try. &lt;a href="https://twitter.com/alvaro_sanchez"&gt;Alvaro Sánchez-Mariscal&lt;/a&gt; introduced a bit of the framework idea and GraalVM project and made a small demo.&lt;/p&gt;

&lt;h3&gt;
  
  
  Jakarta EE 10 Feature by Feature
&lt;/h3&gt;

&lt;p&gt;I haven't used Jakarta (&lt;code&gt;javax&lt;/code&gt;) in a while, so I decided to attend this talk to get a fresh picture of the current development. One thing I liked is the &lt;a href="https://projects.eclipse.org/projects/ee4j.jakartaee-platform/releases/core-profile-10"&gt;"Core Profile"&lt;/a&gt;. It already existed, but they have included a lot of updates, and I like the idea of seeing Java runtimes keep moving to be smaller. You can see all the updates and features on the &lt;a href="https://www.slideshare.net/ivargrimstad/jakarta-ee-10-feature-by-feature-252234724"&gt;slides&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Use Testing to Develop Better Software Faster
&lt;/h3&gt;

&lt;p&gt;Every developer who writes tests as their day-to-day job knows that tests are as necessary as challenging to do and maintain in the right way. Sometimes wrong testing could become an obstacle to development. &lt;a href="https://twitter.com/MaritvanDijk77"&gt;Marit Van Dijk&lt;/a&gt; shared examples and tips. I recommend reading her &lt;a href="https://medium.com/97-things/use-testing-to-develop-better-software-faster-9dd2616543d3"&gt;article on the topic&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Java Next - From Amber to Loom, from Panama to Valhalla
&lt;/h3&gt;

&lt;p&gt;Similar to the talk about Jakarta, Nicolai Parlog (&lt;a href="https://nipafx.dev"&gt;nipafx&lt;/a&gt;) puts together all the ongoing projects to push Java/JVM to the next level. I recommend checking the &lt;a href="https://slides.nipafx.dev/java-next/2022-07-19-jbcnconf/index.html#/"&gt;slides&lt;/a&gt; because it is full of resources. My favorites:&lt;br&gt;
dev.java&lt;br&gt;
inside.java&lt;br&gt;
📼 &lt;a href="https://www.youtube.com/watch?v=KG24inClY2M"&gt;The State of Project Loom with Ron Pressler&lt;/a&gt;&lt;br&gt;
State Of Valhalla: &lt;a href="https://openjdk.org/projects/valhalla/design-notes/state-of-valhalla/01-background"&gt;Part 1&lt;/a&gt;, &lt;a href="https://openjdk.org/projects/valhalla/design-notes/state-of-valhalla/02-object-model"&gt;Part 2&lt;/a&gt;, and &lt;a href="https://openjdk.org/projects/valhalla/design-notes/state-of-valhalla/03-vm-model"&gt;Part 3&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The Hitchhiker’s Guide to a Great Developer Career
&lt;/h3&gt;

&lt;p&gt;Helen and Sven gave a great talk about why it is important to have ownership over our careers. The content was great; they have outstanding delivery skills and are easy to listen to and follow.&lt;/p&gt;

&lt;h3&gt;
  
  
  Talent, passion, curiosity, cooperation and a special spirit for discovery
&lt;/h3&gt;

&lt;p&gt;The closing keynote was a personal history from &lt;a href="https://twitter.com/JaSantaolalla"&gt;Javier Santaolalla&lt;/a&gt; about his experience at &lt;a href="https://home.cern/"&gt;CERN&lt;/a&gt; where he participated in the experiment which led to the Higgs Boson discovery. It was a pleasure to listen to him and to see how hard he lived his profession and passion.&lt;/p&gt;

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

&lt;p&gt;I enjoyed this conference a lot. After almost three years without attending any conference, I have been in DDD Europe and JBCNConf within a month, and I have felt again the motivation that usually comes after an event. I will update the post with videos whenever they are available.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.pexels.com/photo/top-view-of-open-laptop-notebook-and-a-cup-of-coffee-6193649/"&gt;Photo by EKATERINA  BOLOVTSOVA from Pexels&lt;/a&gt;&lt;/p&gt;

</description>
      <category>java</category>
      <category>conference</category>
      <category>talks</category>
      <category>resource</category>
    </item>
    <item>
      <title>Start doing better code reviews tomorrow: The emoji code</title>
      <dc:creator>Juan Vega</dc:creator>
      <pubDate>Thu, 31 Mar 2022 15:30:11 +0000</pubDate>
      <link>https://forem.com/juanvegadev/start-doing-better-code-reviews-tomorrow-the-emoji-code-3jf6</link>
      <guid>https://forem.com/juanvegadev/start-doing-better-code-reviews-tomorrow-the-emoji-code-3jf6</guid>
      <description>&lt;p&gt;One of the software engineer’s main activity nowadays is providing peer feedback about code, but giving written feedback is hard. You mainly face two issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keep it short and straightforward.&lt;/li&gt;
&lt;li&gt;Keep your intention clear.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  It's all about your intention
&lt;/h2&gt;

&lt;p&gt;We work with other people, and we usually don't want to hurt them or make them feel overwhelmed. To help here, and inspired by &lt;a href="https://devblogs.microsoft.com/appcenter/how-the-visual-studio-mobile-center-team-does-code-review/"&gt;a Microsoft post&lt;/a&gt;, we introduced the &lt;strong&gt;emoji code for reviews&lt;/strong&gt; within our team.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Make your comment intention crystal clear with an emoji.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;At the beginning of each comment, you include an emoji that express your intention with that comment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Our code
&lt;/h2&gt;

&lt;p&gt;There are several public examples: &lt;a href="https://github.com/erikthedeveloper/code-review-emoji-guide"&gt;Code Review Emoji Guide&lt;/a&gt;, &lt;a href="https://brendanforster.com/notes/my-code-review-emoji-guide/"&gt;My Code Review Emoji Guide&lt;/a&gt; and &lt;a href="https://gist.github.com/chrisriesgo/818fe94b4f4720eaf7898ccaa48f94c5"&gt;Emoji Code Review Comments&lt;/a&gt;. In my team we use the following one:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;👍: I love this part and want to praise it!&lt;/li&gt;
&lt;li&gt;❓: I have a question or need some insights to understand this part.&lt;/li&gt;
&lt;li&gt;🐛: This is a potential bug. You must provide a detailed explanation and, if possible, a suggested change.&lt;/li&gt;
&lt;li&gt;🤡: A matter of taste, it doesn't affect quality, but from the reviewer's point of view, it will improve code&lt;/li&gt;
&lt;li&gt;💭: Not action required. I want to share a thought, to seed an idea for a future debate. Even not an answer is required.&lt;/li&gt;
&lt;li&gt;♻️: Propose a refactor to simplify or improve a code block. The suggested change is required at least as pseudo-code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is short, and you can memorize it at first sight. Anyone can understand what it means, even without checking the legend.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next step
&lt;/h2&gt;

&lt;p&gt;You can start using this independently or start by proposing any of the existing examples to your team. Don't add too many emojis to the code. Make it easier to remember and be pragmatic.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.pexels.com/photo/emoji-designs-of-golf-balls-on-the-grass-6572958/"&gt;Photo from Pexels&lt;/a&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>codereview</category>
      <category>beginners</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Become a better programmer in 30 minutes.</title>
      <dc:creator>Juan Vega</dc:creator>
      <pubDate>Fri, 18 Feb 2022 06:54:49 +0000</pubDate>
      <link>https://forem.com/juanvegadev/become-a-better-programmer-in-30-minutes-1kb0</link>
      <guid>https://forem.com/juanvegadev/become-a-better-programmer-in-30-minutes-1kb0</guid>
      <description>&lt;p&gt;Well, the title is a little tricky. It will take you 30 minutes every day, but I promise, it is effortless.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I'm not a great programmer; I'm just a good programmer with great habits."&lt;br&gt;
― Kent Beck &lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  How do the first 30 minutes of your day usually look?
&lt;/h2&gt;

&lt;p&gt;Like most software engineers, I usually spend over 60% of my workday (about 5 hours) coding. So &lt;strong&gt;taking away 30 minutes means nothing&lt;/strong&gt; on the total time of the day and the project's whole time.&lt;/p&gt;

&lt;p&gt;You might wonder &lt;em&gt;"why the first 30 minutes and not the last, or somewhere in the middle?"&lt;/em&gt;. Easy, &lt;strong&gt;the actions that I propose take much energy and focus when done well, so it's better to do them when you are still fresh!&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  My 30 minutes
&lt;/h2&gt;

&lt;p&gt;All this practice works well for my current context and environment. You can &lt;strong&gt;start copying me and looking for new ones or adapted versions that fit you.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Self code reviews
&lt;/h3&gt;

&lt;p&gt;I always start the day by doing a code review of the last code that I  wrote. I asked myself what I was trying to achieve, how well the code expressed it, and what comments or clarification an external reviewer might need to understand my decisions. So, I have three steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;What was the intention here? &lt;strong&gt;(Intention)&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;How well is the code expressing it? &lt;strong&gt;(Naming)&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Which comments will help an external reviewer to understand it? &lt;strong&gt;(Comment)&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once I have answered the questions, I start again, using the answers to rethink intention, naming, and comments. For instance, I ask myself: &lt;strong&gt;How can I change code naming to include the value of the additional comment for an external reviewer?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3qWOzqvh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uaj59ss1atednrjt512p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3qWOzqvh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uaj59ss1atednrjt512p.png" alt="small kotlin snippet with arrows pointed to some code where I could apply the previous questions" width="880" height="359"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Starting from scratch
&lt;/h3&gt;

&lt;p&gt;There are always at least two ways of coding the same thing. A good exercise is to ask me every day: What if I use the other approach I discarded?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I don't implement it&lt;/strong&gt;. I usually spend some minutes checking which file would change. The goal is to figure out if I could improve my current solution by evaluating it from another point of view. &lt;strong&gt;I might don't need to change a single line of code.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Among the previous exercise, I also wonder:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;How much code do I write to solve the actual problem? Usually, the simpler the code is, the easier it will be to change if required. &lt;strong&gt;Sometimes we overcomplicate the solutions to cover future use cases that might never arrive.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Change some tests data and code pseudorandomly. I don't do &lt;a href="https://en.wikipedia.org/wiki/Mutation_testing"&gt;mutation testing&lt;/a&gt; with an automated tool, but I manually &lt;strong&gt;delete some lines and change some inputs and expected values to verify how the tests break&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;&lt;strong&gt;Do you dare to test this tomorrow and tell me how it well?&lt;/strong&gt;&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;"No matter the circumstance you can always improve. You can always start improving with yourself. You can always start improving today."&lt;br&gt;
― Kent Beck, Extreme Programming Explained: Embrace Change &lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;p&gt;Firstly, I hear from this practice, with a different approach, of reviewing previous day code from &lt;a href="//carlosble.com"&gt;Carlos Blé&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.pexels.com/photo/wood-art-creative-wall-5799100/"&gt;Photo by Ivan Samkov from Pexels&lt;/a&gt;&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>programming</category>
      <category>career</category>
      <category>challenge</category>
    </item>
    <item>
      <title>What should I choose Next.js or Remix?</title>
      <dc:creator>Juan Vega</dc:creator>
      <pubDate>Tue, 15 Feb 2022 09:27:49 +0000</pubDate>
      <link>https://forem.com/juanvegadev/what-should-i-choose-nextjs-or-remix-41da</link>
      <guid>https://forem.com/juanvegadev/what-should-i-choose-nextjs-or-remix-41da</guid>
      <description>&lt;p&gt;I want to rebuild &lt;a href="//juandavidvega.es"&gt;my personal website&lt;/a&gt;, and I would like to take this as an opportunity to learn something new.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What should I learn &lt;a href="https://nextjs.org/"&gt;Next.js&lt;/a&gt; or &lt;a href="https://remix.run/"&gt;Remix&lt;/a&gt;?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I am a backend developer with a knee on the frontend, mainly React and Typescript. I don't want to learn Vue or any other framework right now.&lt;/p&gt;

&lt;p&gt;I still need to pick a design, so if you have any suggestions.&lt;/p&gt;

&lt;p&gt;I will keep sharing here my progress on building it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.pexels.com/photo/woman-showing-apple-and-bitten-doughnut-6551415/"&gt;Photo by Andres Ayrton from Pexels&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>discuss</category>
      <category>help</category>
    </item>
  </channel>
</rss>
