<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Nikhil Dev Chunchu</title>
    <description>The latest articles on DEV Community by Nikhil Dev Chunchu (@nikhildev).</description>
    <link>https://dev.to/nikhildev</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%2F200817%2F683373df-c242-4669-8c52-2c87844eb006.jpeg</url>
      <title>DEV Community: Nikhil Dev Chunchu</title>
      <link>https://dev.to/nikhildev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nikhildev"/>
    <language>en</language>
    <item>
      <title>Building with Go in a monorepo using Bazel, Gazelle and bzlmod</title>
      <dc:creator>Nikhil Dev Chunchu</dc:creator>
      <pubDate>Sat, 26 Oct 2024 09:02:02 +0000</pubDate>
      <link>https://dev.to/nikhildev/building-with-go-in-a-monorepo-using-bazel-gazelle-and-bzlmod-4on0</link>
      <guid>https://dev.to/nikhildev/building-with-go-in-a-monorepo-using-bazel-gazelle-and-bzlmod-4on0</guid>
      <description>&lt;p&gt;This post is relevant to any engineer who joined in a small-ish company and stuck around long enough to watch it grow, scale, hire more engineers, form more specialised teams. These are good problems to deal with. By the time I quit Google in 2016, it had mastered the infrastructure of being able to support a very large number of engineering population being able to derive benefits from everything historic to still have the capability to grow perhaps 10x or more. One part of the art that Google had mastered is developing in a monorepo. Google internally uses a repo called "google3" which is where all code (at least the one I was aware of resided). This was the mother of everything tech that Google runs. Right from the ability to commit code, getting reviews, cross team/functions/org dependencies, etc. All this was possible by this amazing build system that Google developed called "blaze" and in March 2015 they made it accessible to everyone outside Google under the name "bazel" which is just an anagram for blaze. So, why all this psycho-babble you ask?&lt;/p&gt;

&lt;p&gt;When I joined &lt;a href="https://wolt.com/?ref=nikhildev.com" rel="noopener noreferrer"&gt;Wolt&lt;/a&gt; in 2020 we still called ourselves a startup who still had full autonomy into how, where and what software we wrong. This meant that we had the freedom to pick the language of our choice, the framework, build system and how we deployed. As engineers we had to deal with a lot of boiler plate by ourself over and over, all the way from managing infrastructure to how the binaries are built. At times we spent more time setting things up for a new service than writing the actual useful code.&lt;/p&gt;

&lt;p&gt;Then as we started scaling up came the painful aspect of working with a massive crowd.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;How do we share common code between teams?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;How do we ensure that service although distributed are testable in one go?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;How do we orchestrate releases of feature where there are more than one services involved?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;How do we reduce the friction for engineers to work on cross ownership code?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Any many more&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So the question is how do we build and environment where adopt all the good aspects of a monorepo but also retain the freedom, safety and speed that microservices provide.&lt;/p&gt;

&lt;p&gt;In this post I will try to do my best to document who some of my relearning and hopefully provide a scaffold for anyone else who might be interested.&lt;/p&gt;

&lt;p&gt;The following are the versions this post will be working with.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;bazel&lt;/span&gt; &lt;span class="n"&gt;v7&lt;/span&gt;&lt;span class="m"&gt;.3.1&lt;/span&gt;
&lt;span class="n"&gt;gazelle&lt;/span&gt; &lt;span class="n"&gt;v0&lt;/span&gt;&lt;span class="m"&gt;.39.0&lt;/span&gt;
&lt;span class="n"&gt;rules_go&lt;/span&gt; &lt;span class="n"&gt;v0&lt;/span&gt;&lt;span class="m"&gt;.50.1&lt;/span&gt;
&lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="n"&gt;v1&lt;/span&gt;&lt;span class="m"&gt;.23.1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is purely for the purpose of the latest with best compatibility. Please feel free to comment if I might be mistaken&lt;/p&gt;

&lt;p&gt;There are some simple simple goals we will try to achieve here&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Code is well structured despite being beyond team boundaries&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Reusability is a key. Anything that could be helpful to other teams should be placed in a common places and relevant definition need to be there to make it usable like a library&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You should be able to run any part of the monorepo locally without much knowledge of setting up SDKs, compilers, etc.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This post will not cover how to deploy the binaries.&lt;/p&gt;




&lt;p&gt;The name of the repo that I have created for the sake of this post os called"basil". You can call it whatever you want.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1&lt;/strong&gt;: Starting Bazel 6.3 you do not need to have a &lt;a href="https://bazel.build/external/migration?ref=nikhildev.com" rel="noopener noreferrer"&gt;WORKSPACE file anymore&lt;/a&gt;. So, we will start with creating the following at the root of the repo&lt;/p&gt;

&lt;p&gt;&lt;em&gt;MODULE.bazel&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"basil"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.0.0"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;http_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;use_repo_rule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"@bazel_tools//tools/build_defs/repo:http.bzl"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"http_file"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;http_archive&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;use_repo_rule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"@bazel_tools//tools/build_defs/repo:http.bzl"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"http_archive"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="n"&gt;https&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="c"&gt;//github.com/bazelbuild/rules_go/blob/master/docs/go/core/bzlmod.md&lt;/span&gt;
&lt;span class="n"&gt;bazel_dep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"rules_go"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.41.0"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;bazel_dep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"gazelle"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.37.0"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;GO_VERSION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"1.23.1"&lt;/span&gt;

&lt;span class="n"&gt;go_sdk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;use_extension&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"@rules_go//go:extensions.bzl"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"go_sdk"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;go_sdk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;download&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GO_VERSION&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;go_deps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;use_extension&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"@gazelle//:extensions.bzl"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"go_deps"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;go_deps&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;go_mod&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"//:go.mod"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;use_repo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;go_deps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"com_github_google_go_cmp"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The module file does the following&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Define the report for &lt;a href="https://github.com/bazelbuild/rules_go?ref=nikhildev.com" rel="noopener noreferrer"&gt;rules_go&lt;/a&gt; and &lt;a href="https://github.com/bazelbuild/bazel-gazelle?ref=nikhildev.com" rel="noopener noreferrer"&gt;gazelle&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Define the version of go that we will be using for the SDK and the gazelle extensions for the same&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; Then some more boilerplate in the BUILD file&lt;/p&gt;

&lt;p&gt;&lt;em&gt;BUILD&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"@gazelle//:def.bzl"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"gazelle"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;gazelle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"gazelle"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;gazelle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"gazelle-update-repos"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s"&gt;"-from_file=go.mod"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"-to_macro=deps.bzl%go_dependencies"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"-prune"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;command&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"update-repos"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;This also ensure that dependencies from go.mod from the role are provided&lt;/p&gt;

&lt;p&gt;Now that this is out of way we need to decide what kind of a code organisation we would like to have. For the sake of this post we will assume that all of the code will be in a directory called &lt;code&gt;packages&lt;/code&gt; , we have two team who work adjacently called &lt;code&gt;ledgering&lt;/code&gt; and &lt;code&gt;invoicing&lt;/code&gt; who can share code between themselves as well as from common &lt;code&gt;libs&lt;/code&gt; package. This sort of hierarchy is fairly common and extensible.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3:&lt;/strong&gt; So let's create a directory called &lt;code&gt;packages&lt;/code&gt; Next, run the following at the root&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bazel run //:gazelle
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Gazelle is a build file generator for bazel. Writing these files with dependencies, naming, etc can get quite cumbersome. Gazelle can inspect your code and add the necessary library dependencies it the build file.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4:&lt;/strong&gt; Next, in the root of the monorepo, run the following to initialise the &lt;code&gt;go.mod&lt;/code&gt; file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go mod init basil
go mody tidy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;strong&gt;Step 5:&lt;/strong&gt; Alright, now we are ready to write to actual code. I will start by creating the following directories in the &lt;code&gt;packages&lt;/code&gt; directory&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;libs&lt;/code&gt; to hold all common libraries which could be nested if needed as well&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;invoicing&lt;/code&gt; for the first team which can serve as a provider for invoices&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;ledgering&lt;/code&gt; for the second team that can serve as a consumer for invoices&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Step 6:&lt;/strong&gt; Let's start with the common libs directory which can potential hold some formatting functions. Let's create another directory called &lt;code&gt;common&lt;/code&gt; in libs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 7:&lt;/strong&gt; Next in the formatters directory create a file called &lt;code&gt;invoice_id_generator.go&lt;/code&gt; and place the following code in that&lt;br&gt;
&lt;/p&gt;

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

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

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;GetInvoiceId&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sprint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rand&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Now when you run &lt;code&gt;bazel run //:gazelle&lt;/code&gt; on the root, you can see that Gazelle creates a build for you automatically.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"@rules_go//go:def.bzl"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"go_library"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;go_library&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"formatters"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;srcs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"concatinator.go"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;importpath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"basil/packages/libs/formatters"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;visibility&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"//visibility:public"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Gazelle by default creates a &lt;code&gt;go_library&lt;/code&gt; but we will see what else we can create manually later&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 8:&lt;/strong&gt; Now that we have something useful let's imagine that ledgering team is requesting the invoicing team to create an invoices which in turn uses a common library from libs to generate the id.&lt;/p&gt;

&lt;p&gt;We start by creating a file called &lt;code&gt;invoice.go&lt;/code&gt; in the team's root with the following content&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"math/rand"&lt;/span&gt;
    &lt;span class="s"&gt;"time"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Invoice&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ID&lt;/span&gt;        &lt;span class="kt"&gt;int64&lt;/span&gt;
    &lt;span class="n"&gt;Amount&lt;/span&gt;    &lt;span class="kt"&gt;float64&lt;/span&gt;
    &lt;span class="n"&gt;CreatedAt&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Time&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;GetNewInvoice&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Invoice&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Invoice&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="kt"&gt;int64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rand&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
        &lt;span class="n"&gt;Amount&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="n"&gt;rand&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Float64&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;CreatedAt&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then another file called &lt;code&gt;booker.go&lt;/code&gt; in ledgering with the following contents&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;lib&lt;/span&gt; &lt;span class="s"&gt;"basil/packages/fintech/invoicing"&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;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;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;lib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetNewInvoice&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Generate new Invoice with ID:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now when you run &lt;code&gt;blaze run //:gazelle&lt;/code&gt; you will notice that gazelle has create the libaries along with the dependencies that the go code uses as well.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 9:&lt;/strong&gt; Now, we want the make the &lt;code&gt;booker.go&lt;/code&gt; an executable since it might be invoked as a standalone. Since gazelle has create a binary for us, let's create one with the following contents in the BUILD.bazel. So, your final build file should look like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"@rules_go//go:def.bzl"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"go_binary"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"go_library"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;go_binary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"app"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;embed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;":lib"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;importpath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"basil/packages/fintech/ledgering_app"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;visibility&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"//visibility:public"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;go_library&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"lib"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;srcs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"booker.go"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;importpath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"basil/packages/fintech/ledgering"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;visibility&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"//visibility:private"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;deps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"//packages/fintech/invoicing:lib"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 10:&lt;/strong&gt; Now its time to take this whole thing for a spin. You can build and run the binary as shows below&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bazel build //packages/ledgering:app
bazel run //packages/ledgering:app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If all went fine, you should see something like below&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;INFO&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Analyzed&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt; &lt;span class="c"&gt;//packages/ledgering:app (1 packages loaded, 5 targets configured).&lt;/span&gt;
&lt;span class="n"&gt;INFO&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Found&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="n"&gt;Target&lt;/span&gt; &lt;span class="c"&gt;//packages/ledgering:app up-to-date:&lt;/span&gt;
  &lt;span class="n"&gt;bazel&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;packages&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;ledgering&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;app_&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;
&lt;span class="n"&gt;INFO&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Elapsed&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.747&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Critical&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.52&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;
&lt;span class="n"&gt;INFO&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt; &lt;span class="n"&gt;processes&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="n"&gt;internal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt; &lt;span class="n"&gt;darwin&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;sandbox&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;INFO&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Build&lt;/span&gt; &lt;span class="n"&gt;completed&lt;/span&gt; &lt;span class="n"&gt;successfully&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="n"&gt;actions&lt;/span&gt;
&lt;span class="n"&gt;INFO&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Running&lt;/span&gt; &lt;span class="n"&gt;command&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;bazel&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;packages&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;ledgering&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;app_&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;
&lt;span class="n"&gt;Generate&lt;/span&gt; &lt;span class="nb"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Invoice&lt;/span&gt; &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;7213007727604049262&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;And there you are. You no longer have to create individual repositories for each project that you might be working on but instead use same repo and libraries across projects. I will probably write a follow up post to this on how you can build binaries through CI/CD for this setup.&lt;/p&gt;

&lt;p&gt;Please do leave comments in case I have mispoken or misunderstand anything. You can find all the code I have demoed above in my repo&lt;/p&gt;

</description>
      <category>bazel</category>
      <category>monrepo</category>
      <category>go</category>
      <category>gazelle</category>
    </item>
    <item>
      <title>Self hosting ghost blog on your home server</title>
      <dc:creator>Nikhil Dev Chunchu</dc:creator>
      <pubDate>Thu, 24 Oct 2024 12:16:09 +0000</pubDate>
      <link>https://dev.to/nikhildev/self-hosting-ghost-blog-on-your-home-server-37b5</link>
      <guid>https://dev.to/nikhildev/self-hosting-ghost-blog-on-your-home-server-37b5</guid>
      <description>&lt;p&gt;If you are reading this post, then you need to be aware that this post is at the moment running on my own server at home.&lt;/p&gt;

&lt;p&gt;Ghost has one of most beautiful interface for both writing and reading posts. The only downside that I found was that to leverage the full benefit I had to pay up. Of course Wordpress is an option to self host but only recently did I become aware that Ghost can be self hosted as well and has docker images that I could use.&lt;/p&gt;

&lt;p&gt;In this post I will try my best to explaining how I got mine up and working&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prerequisites&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I will assume that you have the following already set up&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A linux server&lt;/li&gt;
&lt;li&gt;Docker installed within it&lt;/li&gt;
&lt;li&gt;Cloudflare tunnel or some way of exposing your service to the outside world&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's start with the &lt;code&gt;docker-compose.yaml&lt;/code&gt;&lt;br&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;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;ghost&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ghost:latest&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ghost-server&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;url=https://blog.nikhildev.com&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;mail__transport=SMTP&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;mail__options__host=smtp.eu.mailgun.org&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;mail__options__port=587&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;mail__options__auth__user=&amp;lt;your_auth_user_email&amp;gt;&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;mail__options__auth__pass=&amp;lt;the_password_from_mailgun&amp;gt;&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;mail__from=blog@example.com&lt;/span&gt; &lt;span class="c1"&gt;#This probably will need verification&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;database__client=mysql&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;database__connection__host=db&lt;/span&gt; &lt;span class="c1"&gt;# Name of the service from below&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;database__connection__user=ghost&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;database__connection__password=ghost&lt;/span&gt; &lt;span class="c1"&gt;# You can choose a better password&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;database__connection__database=ghost&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/docker_volumes/ghost/content:/var/lib/ghost/content&lt;/span&gt; &lt;span class="c1"&gt;# I prefer to map a different location for the safe keep of the data. The /docker_volumes is mirrored on zpool for redundancy&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;db&lt;/span&gt;
    &lt;span class="na"&gt;ports&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;2368:2368"&lt;/span&gt;
  &lt;span class="na"&gt;db&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mysql:9&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ghost-db&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;default&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/docker_volumes/ghost/db/data:/var/lib/mysql&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;MYSQL_DATABASE=ghost&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;MYSQL_USER=ghost&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;MYSQL_PASSWORD=ghost&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;MYSQL_ROOT_PASSWORD=ghost&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;MYSQL_ROOT_HOST=172.*.*.*&lt;/span&gt; &lt;span class="c1"&gt;## Ensures the Docker network has access&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above docker compose files we are defining two service. &lt;code&gt;ghost-server&lt;/code&gt; is the main application and db is a mysql instance that it would be using.&lt;/p&gt;

&lt;p&gt;Now, if you want the newsletter part of ghost be available to you, I would highly recommend signing up with something like Mailgun which has pretty generous free tier. Once you have signed up, use the generated credentials in ghost. Here is a great article on it &lt;a href="https://brightthemes.com/blog/ghost-mailgun-config" rel="noopener noreferrer"&gt;https://brightthemes.com/blog/ghost-mailgun-config&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'm sticking to exposing the default port of the service as in my entire stack there are no conflicting ones.&lt;/p&gt;

&lt;p&gt;The db service is vanilla mysql without any bells and whistles. The official guide suggests using mysql 8 but I've tried this with mysql 9 and it works just fine. We simply are setting the credentials before hand to be made accessible to the ghost instance. Even here I prefer to mount a redundant local directory just for safe keeps.&lt;/p&gt;

&lt;p&gt;And that's it. Go ahead and run the container with docker compose up -d and run through the setup process. If you run into any issues, please leave a comment and I can probably try and help you out&lt;/p&gt;

</description>
      <category>selfhosting</category>
      <category>ghostblog</category>
    </item>
    <item>
      <title>A practical example of shared libraries in a monorepo</title>
      <dc:creator>Nikhil Dev Chunchu</dc:creator>
      <pubDate>Thu, 24 Oct 2024 08:48:44 +0000</pubDate>
      <link>https://dev.to/nikhildev/a-practical-example-of-shared-libraries-in-a-monorepo-2548</link>
      <guid>https://dev.to/nikhildev/a-practical-example-of-shared-libraries-in-a-monorepo-2548</guid>
      <description>&lt;p&gt;One of the most powerful aspects of working in a monorepo is the ability to share code between packages/teams/hierarchies. In this post I will try to explain a very simple real world scenario&lt;/p&gt;

&lt;h1&gt;
  
  
  Example Scenario
&lt;/h1&gt;

&lt;p&gt;Imagine you want to develop a library to show the file sizes in megabytes which you feel might be useful to other parts of your monorepo. The library accepts the size as Integer (ex: 2048 bytes) and can return a humanized string (ex: 2 MB). To add some quality assurance, we will also write a test for the same.&lt;/p&gt;

&lt;h1&gt;
  
  
  How does Bazel enable sharing code?
&lt;/h1&gt;

&lt;p&gt;From the above scenario we are aware that we need to develop this function as a shared library which then be imported by another package for usage. Bazel makes this extremely simple by allowing us to define the function in a library and export it other services that will need it. As explained in my earlier post linked at the bottom of this post, we can also control which other libraries can be allowed to import it for usage as well.&lt;/p&gt;

&lt;h1&gt;
  
  
  Let's get coding
&lt;/h1&gt;

&lt;p&gt;For code organization purpose, we will have a libraries directory at the root of our workspace with a child directory called &lt;code&gt;humanize_filesize&lt;/code&gt; which is where we will write our library code.&lt;/p&gt;

&lt;p&gt;Let's write some very elementary Go code in &lt;code&gt;humanize_filesize.go&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s"&gt;"fmt"&lt;/span&gt;

&lt;span class="c"&gt;// GetHumanizedFilesize takes size_in_bytes as an int32 pointer and returns the size in megabytes.&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;GetHumanizedFilesize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size_in_bytes&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="kt"&gt;int32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;string&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;size_in_bytes&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;size_in_megabytes&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="kt"&gt;float64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;size_in_bytes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1024&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="m"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%.4f MB"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size_in_megabytes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"0 MB"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code simply takes an int32 as an input and returns a computed readable megabyte string to 4 decimal precision&lt;/p&gt;

&lt;p&gt;This function is definitely not comprehensive and can definitely be improved, but that's not the point of this exercise.&lt;/p&gt;

&lt;p&gt;Also assert that our logic is working as intended, we will add a very elementary test alongside our go code in a file called &lt;code&gt;humanize_filesize_test.go&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

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

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

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;TestHumanizeFilesize&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="n"&gt;tests&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;name&lt;/span&gt;          &lt;span class="kt"&gt;string&lt;/span&gt;
        &lt;span class="n"&gt;size_in_bytes&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="kt"&gt;int32&lt;/span&gt;
        &lt;span class="n"&gt;expected&lt;/span&gt;      &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="p"&gt;}{&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;          &lt;span class="s"&gt;"nil bytes"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;size_in_bytes&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;expected&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;      &lt;span class="s"&gt;"0 MB"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;          &lt;span class="s"&gt;"2048 bytes"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;size_in_bytes&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;int32Ptr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;2048&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;expected&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;      &lt;span class="s"&gt;"0.0020 MB"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;          &lt;span class="s"&gt;"0 bytes"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;size_in_bytes&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;int32Ptr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;expected&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;      &lt;span class="s"&gt;"0.0000 MB"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tt&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;tests&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;Run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;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="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;GetHumanizedFilesize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size_in_bytes&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;result&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;tt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expected&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;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"expected %s, got %s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;int32Ptr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="kt"&gt;int32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="kt"&gt;int32&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A very simple test with basic tests for nil, int32 and 0 as inputs&lt;/p&gt;

&lt;p&gt;Now comes the juicy part of how to export this function so that this can be imported within other packages or services. This is where we have to define the &lt;code&gt;BUILD.bazel&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;load("@rules_go//go:def.bzl", "go_library", "go_test")

go_library(
    name = "humanize_filesize",
    srcs = ["humanize_filesize.go"],
    importpath = "basil/libraries/humanize_filesize",
    visibility = ["//visibility:public"],
)

go_test(
    name = "humanize_filesize_test",
    srcs = ["humanize_filesize_test.go"],
    embed = [":humanize_filesize"],
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In here we are defining two main rules. One for the actual library and one for the test file that we wrote.&lt;/p&gt;

&lt;p&gt;The go_library defines that the target humanize_filesize uses humanize_filesize.go as one of its sources which can be imported by the path specified in importpath and it is visible publicly within the workspace for other packages to import. We will learn how to control the visibility in a future post.&lt;/p&gt;

&lt;p&gt;The go_test defines a test target which embeds the code from the output of go_library.&lt;/p&gt;

&lt;p&gt;At this point we should be able to test the library by running our test suite as following&lt;/p&gt;

&lt;p&gt;bazel build //... &amp;amp;&amp;amp; bazel run //libraries/humanize_filesize:humanize_filesize_test&lt;/p&gt;

&lt;p&gt;You should be able to see the test output as following indicating that all the tests have passed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INFO: Analyzed target //libraries/humanize_filesize:humanize_filesize_test (0 packages loaded, 0 targets configured).
INFO: Found 1 target...
Target //libraries/humanize_filesize:humanize_filesize_test up-to-date:
  bazel-bin/libraries/humanize_filesize/humanize_filesize_test_/humanize_filesize_test
INFO: Elapsed time: 0.392s, Critical Path: 0.24s
INFO: 5 processes: 1 internal, 4 darwin-sandbox.
INFO: Build completed successfully, 5 total actions
INFO: Running command line: external/bazel_tools/tools/test/test-setup.sh libraries/humanize_filesize/humanize_filesize_test_/humanize_filesize_test
exec ${PAGER:-/usr/bin/less} "$0" || exit 1
Executing tests from //libraries/humanize_filesize:humanize_filesize_test
-----------------------------------------------------------------------------
PASS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🎉 Wohoo!!! 🥳 Now we know that our library is working as intended.&lt;/p&gt;

&lt;p&gt;Now let's use this library in a service service1 within a services directory that we will create at the root of the workspace with the following go code and &lt;code&gt;BUILD.bazel&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;service1.go&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"basil/libraries/humanize_filesize"&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"math/rand"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;rand&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Int31n&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1000000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;`%d bytes = %s\n`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;humanize_filesize&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetHumanizedFilesize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;BUILD.bazel&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;load("@rules_go//go:def.bzl", "go_binary", "go_library")

go_library(
    name = "service1_lib",
    srcs = ["service1.go"],
    importpath = "basil/services/service1",
    visibility = ["//visibility:private"],
    deps = ["//libraries/humanize_filesize"],
)

go_binary(
    name = "service1",
    embed = [":service1_lib"],
    visibility = ["//visibility:public"],
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The go code is pretty simple which imports our library that we declared earlier and uses the &lt;code&gt;GetHumanizedFilesize&lt;/code&gt; function from our library and passes a random integer value and prints the output.&lt;/p&gt;

&lt;p&gt;Now when execute bazel build &lt;code&gt;//services/service1&lt;/code&gt; , bazel will resolve all dependencies for our target including the library that we developed and build them.&lt;/p&gt;

&lt;p&gt;service1 can now be executed using &lt;code&gt;bazel run //services/service1&lt;/code&gt; since we have only one binary target defined. If you have more than one binary targets, ex: serviceX, you can execute that using &lt;code&gt;bazel run //services/service1:serviceX&lt;/code&gt;. By default when not specifying a target, bazel will always try to find a binary target with the same name as the directory and run that.&lt;/p&gt;

&lt;p&gt;So... there you go. We have made your first shared library that can be used by other parts of our monorepo.&lt;/p&gt;

&lt;p&gt;All code for this example can be found at &lt;a href="https://github.com/nikhildev/basil/pull/3/commits/61c673b8757860bd5e60eb2ab6c35f3f4da78c87" rel="noopener noreferrer"&gt;https://github.com/nikhildev/basil/pull/3/commits/61c673b8757860bd5e60eb2ab6c35f3f4da78c87&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you like the content of this post feel free to share it. Also, please subscribe and leave comments on what you think about this post and if there are things that you would like to see me improving on.&lt;/p&gt;

</description>
      <category>go</category>
      <category>bazel</category>
      <category>monorepo</category>
    </item>
    <item>
      <title>Simple hello world program using Bazel and Go lang</title>
      <dc:creator>Nikhil Dev Chunchu</dc:creator>
      <pubDate>Thu, 24 Oct 2024 08:39:43 +0000</pubDate>
      <link>https://dev.to/nikhildev/simple-hello-world-program-using-bazel-and-go-lang-2anc</link>
      <guid>https://dev.to/nikhildev/simple-hello-world-program-using-bazel-and-go-lang-2anc</guid>
      <description>&lt;p&gt;After writing the post on "&lt;a href="https://nikhildev.com/building-with-go-in-a-monorepo-using-bazel-gazelle-and-bzlmod" rel="noopener noreferrer"&gt;Building with Go in a monorepo using Bazel, Gazelle and bzlmod&lt;/a&gt;" and sharing with some colleagues, I saw some growing interest in monorepo development. I learnt that not many people were still experienced enough to understand the benefits it could bring. So I decided to convert this to a series starting with this post on &lt;a href="https://nikhildev.com/simple-hello-world-program-using-bazel-and-go-lang/" rel="noopener noreferrer"&gt;Simple hello world program using Bazel and Go lang&lt;br&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this post, I will try to cover some extremely basic concepts of Bazel along with code snippets to get someone up and going in no time.&lt;/p&gt;
&lt;h1&gt;
  
  
  What is Bazel?
&lt;/h1&gt;

&lt;p&gt;According to the &lt;a href="https://bazel.build/about/intro" rel="noopener noreferrer"&gt;official documentation&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Bazel is an open-source build and test tool similar to Make, Maven, and Gradle. It uses a human-readable, high-level build language. Bazel supports projects in multiple languages and builds outputs for multiple platforms. Bazel supports large codebases across multiple repositories, and large numbers of users.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It has been in use at Google for decades and is thoroughly battle tested. You can read more about how I became aware of this on the post linked above.&lt;/p&gt;


&lt;h1&gt;
  
  
  Step 1: Setting up repository
&lt;/h1&gt;

&lt;p&gt;For the purpose of this series I have created a repository at &lt;a href="//github.com/nikhildev/basil"&gt;github.com/nikhildev/basil&lt;/a&gt; which will evolve over time. At the time of writing this post, the latest commit is &lt;a href="https://github.com/nikhildev/basil/tree/d8af2aea6fb8b692f735f9959429df9fcd28422b" rel="noopener noreferrer"&gt;https://github.com/nikhildev/basil/tree/d8af2aea6fb8b692f735f9959429df9fcd28422b&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, go ahead an create a new git repo on any provider you want to choose&lt;/p&gt;

&lt;p&gt;For the &lt;code&gt;.gitignore&lt;/code&gt; I highly recommend adding the following so as to not include unnecessary files in your commits&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# If you prefer the allow list template instead of the deny list, see community template:
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
#
bazel-*
/.ijwb
/.clwb
/.idea
/.project
*.swp
/.bazelrc.user

# macOS-specific excludes
.DS_Store

# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib

# Test binary, built with `go test -c`
*.test

# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# Dependency directories (remove the comment below to include it)
# vendor/

# Go workspace file
go.work
go.work.sum

# env file
.env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  Step 2: Adding some Bazel boilerplate
&lt;/h1&gt;

&lt;p&gt;Starting Bazel 6.3 you do not need to have a &lt;a href="https://bazel.build/external/migration?ref=nikhildev.com" rel="noopener noreferrer"&gt;WORKSPACE&lt;/a&gt; file anymore. So, we will start with creating the following at the root of the repo&lt;/p&gt;

&lt;p&gt;&lt;code&gt;MODULE.bazel&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;"""Building go applications in a monorepo environment"""

module(name = "basil", version = "0.0.0")
http_file = use_repo_rule(
    "@bazel_tools//tools/build_defs/repo:http.bzl", "http_file"
)
http_archive = use_repo_rule(
    "@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive"
)

# https://github.com/bazelbuild/rules_go/blob/master/docs/go/core/bzlmod.md
bazel_dep(name = "rules_go", version = "0.50.1")
bazel_dep(name = "gazelle", version = "0.39.1")

GO_VERSION = "1.23.2"

go_sdk = use_extension("@rules_go//go:extensions.bzl", "go_sdk")
go_sdk.download(version = GO_VERSION)

go_deps = use_extension("@gazelle//:extensions.bzl", "go_deps")
go_deps.from_file(go_mod = "//:go.mod")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is where we set the go version we want to use as well as the gazelle and rules_go version.&lt;/p&gt;

&lt;p&gt;We will be using Gazelle for BUILD file management. Gazelle makes generating build file quite convenient. You can read more about it &lt;a href="https://github.com/bazel-contrib/bazel-gazelle" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;BUILD.bazel&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;load("@gazelle//:def.bzl", "gazelle")

gazelle(name = "gazelle")

gazelle(
    name = "gazelle-update-repos",
    args = [
        "-from_file=go.mod",
        "-to_macro=deps.bzl%go_dependencies",
        "-prune",
    ],
    command = "update-repos",
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This should be at the root of the repo. This instructs gazelle on the source of the go mod file and other processes to run&lt;/p&gt;

&lt;p&gt;Next we create 3 more files with the respective contents. Don't worry about what these do for now.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;.bazelignore&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;runfiles/
bzlmod/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;.bazelrc&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;# Enable Bzlmod for every Bazel command
common --enable_bzlmod
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;.bazelversion&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;7.3.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alright, by this point you should have everything that's needed to get some basics working. Now if you run &lt;code&gt;bazel build //...&lt;/code&gt; at the root, bazel should be able traverse through the repo and build any packages that it discovers. Bazel caches package outputs from earlier builds, so any subsequent build from here on should be extremely fast.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next up, let's get cracking on that actual hello world program.
&lt;/h2&gt;

&lt;h1&gt;
  
  
  Step 3: Writing a helloworld package
&lt;/h1&gt;

&lt;p&gt;For the basic organisation of code we will be writing all the Go code in a directory called &lt;code&gt;/packages&lt;/code&gt;. This way any references in other parts of the code can refer to it as &lt;code&gt;//packages/...&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Let's create a directory in the packages directory called &lt;code&gt;helloworld&lt;/code&gt; and add the following&lt;/p&gt;

&lt;p&gt;&lt;code&gt;helloworld.go&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;package main

import (
    "fmt"
)

func main() {
    fmt.Println("Hello World!")
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;BUILD.bazel&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;load("@rules_go//go:def.bzl", "go_binary", "go_library")

go_binary(
    name = "helloworld",
    embed = [":helloworld_lib"],
    importpath = "basil/packages/helloworld",
    visibility = ["//visibility:private"],
)

go_library(
    name = "helloworld_lib",
    srcs = ["helloworld.go"],
    importpath = "basil/packages/helloworld_lib",
    visibility = ["//visibility:private"],
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The go program is fairly simple. We have a simple main function that simple prints a hello world message.&lt;/p&gt;

&lt;p&gt;The bit we are interested in is the &lt;code&gt;BUILD.bazel&lt;/code&gt; file. Let's go over this block by block and try to understand what it means.&lt;/p&gt;

&lt;p&gt;The first line loads the go_binary and go_library macros from &lt;code&gt;rules_go&lt;/code&gt; . You will find these being used later in the code.&lt;/p&gt;

&lt;p&gt;In line 10 we are defining a library called &lt;code&gt;helloworld_lib&lt;/code&gt; and specify the sources of the library as &lt;code&gt;helloworld.go&lt;/code&gt;. If this package needs to be me made importable, then you can also specify the path it will be available at which is &lt;code&gt;basil/packages/helloworld_lib&lt;/code&gt;. Then comes the visibility and here we are specifying that the library is private only visible within this package. In the future posts we might look into how to change these parameters to use dependencies from other packages.&lt;/p&gt;

&lt;p&gt;In line 3 we then use the go_binary macro from rules_go to generate a binary for our program. Here we specify the library that we have defined earlier in the embed parameter. The package visibility applies to binaries as well.&lt;/p&gt;




&lt;h1&gt;
  
  
  Step 4: Build and Run
&lt;/h1&gt;

&lt;p&gt;And that's it. Voila! You can run the binary by first building it using bazel build &lt;code&gt;//packages/helloworld&lt;/code&gt; followed by &lt;code&gt;bazel run //packages/helloworld&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If you like this post and would like to get future posts that build upon this as a part of the series, please subscribe and share this post.&lt;/p&gt;

</description>
      <category>go</category>
      <category>bazel</category>
      <category>monorepo</category>
    </item>
  </channel>
</rss>
