<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Go Package &amp; Module Architecture on Atharva Pandey</title><link>https://atharva.page/series/go-package--module-architecture/</link><description>Recent content in Go Package &amp; Module Architecture on Atharva Pandey</description><generator>Hugo</generator><language>en-us</language><copyright>Copyright ©</copyright><lastBuildDate>Tue, 25 Feb 2025 00:00:00 +0000</lastBuildDate><atom:link href="https://atharva.page/series/go-package--module-architecture/index.xml" rel="self" type="application/rss+xml"/><item><title>Lesson 7: go.work and Workspace Mode — Developing multiple modules without replace hacks</title><link>https://atharva.page/post/go/go-pkg-workspaces/</link><pubDate>Tue, 25 Feb 2025 00:00:00 +0000</pubDate><guid>https://atharva.page/post/go/go-pkg-workspaces/</guid><description>&lt;p&gt;Before &lt;code&gt;go.work&lt;/code&gt; existed, developing across multiple local Go modules was genuinely painful. You were working on a library in one directory and an application that depended on it in another. Every time you changed the library, you had to add a &lt;code&gt;replace&lt;/code&gt; directive to the application&amp;rsquo;s &lt;code&gt;go.mod&lt;/code&gt;, run your tests, and then remember — always remember — to remove the &lt;code&gt;replace&lt;/code&gt; before committing. I have seen &lt;code&gt;replace&lt;/code&gt; directives committed to production &lt;code&gt;go.mod&lt;/code&gt; files more times than I would like to admit.&lt;/p&gt;</description></item><item><title>Lesson 6: Dependency Direction — Always depend inward, never outward</title><link>https://atharva.page/post/go/go-pkg-dependency-direction/</link><pubDate>Thu, 02 Jan 2025 00:00:00 +0000</pubDate><guid>https://atharva.page/post/go/go-pkg-dependency-direction/</guid><description>&lt;p&gt;We fixed cycles in lesson 2. But removing cycles is a necessary condition for good architecture, not a sufficient one. You can have a perfectly cycle-free dependency graph where every arrow points in the wrong direction, and the result is still a system that is painful to test, extend, and reason about.&lt;/p&gt;
&lt;p&gt;Dependency direction is about more than just &amp;ldquo;does A import B.&amp;rdquo; It is about which layer owns the core business rules and which layers are allowed to know about which other layers. Get this wrong and your domain logic ends up tangled with your database driver. Get it right and you can replace your entire HTTP layer, or swap Postgres for a different store, without changing a single line of business logic.&lt;/p&gt;</description></item><item><title>Lesson 5: Monolith vs Multi-Module — One go.mod or many? It depends.</title><link>https://atharva.page/post/go/go-pkg-mono-vs-multi/</link><pubDate>Fri, 08 Nov 2024 00:00:00 +0000</pubDate><guid>https://atharva.page/post/go/go-pkg-mono-vs-multi/</guid><description>&lt;p&gt;When I first started structuring larger Go projects, I defaulted to what felt natural: one repository, one &lt;code&gt;go.mod&lt;/code&gt;. It worked for a long time. Then I joined a project with four services in a single repo, each with different dependency requirements, and suddenly the single &lt;code&gt;go.mod&lt;/code&gt; was pulling in every dependency of every service for every build. Tests for the email service were slow because the &lt;code&gt;go test&lt;/code&gt; run was loading the ML inference library needed only by the recommendation service. That was when I started thinking seriously about module boundaries, not just package boundaries.&lt;/p&gt;</description></item><item><title>Lesson 4: Interface Placement — Define interfaces where they're used, not where they're implemented</title><link>https://atharva.page/post/go/go-pkg-interface-placement/</link><pubDate>Sat, 28 Sep 2024 00:00:00 +0000</pubDate><guid>https://atharva.page/post/go/go-pkg-interface-placement/</guid><description>&lt;p&gt;This is the Go idiom that surprises people coming from Java or C# the most. In those languages, you define an interface in the same place as (or even before) the implementation, and consumers import the interface. Go&amp;rsquo;s approach is the exact opposite, and it takes a while to internalize why. Once it clicks, it changes how you think about dependencies fundamentally.&lt;/p&gt;
&lt;p&gt;The rule is: define an interface in the package that needs it, not in the package that satisfies it. It sounds backwards. It is not. It is one of the most powerful design decisions the language enables.&lt;/p&gt;</description></item><item><title>Lesson 3: internal/ Usage Patterns — Compiler-enforced encapsulation</title><link>https://atharva.page/post/go/go-pkg-internal/</link><pubDate>Sun, 18 Aug 2024 00:00:00 +0000</pubDate><guid>https://atharva.page/post/go/go-pkg-internal/</guid><description>&lt;p&gt;Go does not have access modifiers in the Java or Kotlin sense. There is no &lt;code&gt;protected&lt;/code&gt;, no &lt;code&gt;package-private&lt;/code&gt;, no friend classes. You get exactly two visibility levels: exported (capital letter) and unexported (lowercase letter). That simplicity is a feature. But it creates a gap: how do you share something across packages within your own module without accidentally exposing it to the outside world?&lt;/p&gt;
&lt;p&gt;The answer is &lt;code&gt;internal/&lt;/code&gt;. It is one of the most underused and underappreciated tools in Go&amp;rsquo;s package system, and it is enforced by the compiler itself.&lt;/p&gt;</description></item><item><title>Lesson 2: Avoiding Cyclic Dependencies — If packages import each other, your design is wrong</title><link>https://atharva.page/post/go/go-pkg-cyclic-deps/</link><pubDate>Fri, 12 Jul 2024 00:00:00 +0000</pubDate><guid>https://atharva.page/post/go/go-pkg-cyclic-deps/</guid><description>&lt;p&gt;The Go compiler refuses to build a program with a cyclic import. It is not a warning. It is not a lint violation. It is a hard failure. I used to find this annoying when I was newer to the language. Now I consider it one of Go&amp;rsquo;s greatest gifts. The compiler is telling you something important: if package A needs package B and package B needs package A, you have not yet understood what the relationship between these two concepts really is.&lt;/p&gt;</description></item><item><title>Lesson 1: Package Boundaries — One package, one responsibility, zero excuses</title><link>https://atharva.page/post/go/go-pkg-boundaries/</link><pubDate>Mon, 03 Jun 2024 00:00:00 +0000</pubDate><guid>https://atharva.page/post/go/go-pkg-boundaries/</guid><description>&lt;p&gt;I have refactored a lot of Go codebases — my own included. And the single most common structural mistake I see is the &lt;code&gt;utils&lt;/code&gt; package. Or &lt;code&gt;helpers&lt;/code&gt;. Or &lt;code&gt;common&lt;/code&gt;. Or sometimes the worst offender of all: a package named after the entire application domain with fifty unrelated files sitting inside it. When I first started writing Go seriously, I made all of these mistakes. This lesson is about why package boundaries matter and how to get them right.&lt;/p&gt;</description></item></channel></rss>