<?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: shanjunmei</title>
    <description>The latest articles on DEV Community by shanjunmei (@shanjunmei).</description>
    <link>https://dev.to/shanjunmei</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4020647%2F3d607c9e-cb07-4d7d-9f6f-7133f5ee595a.png</url>
      <title>DEV Community: shanjunmei</title>
      <link>https://dev.to/shanjunmei</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/shanjunmei"/>
    <language>en</language>
    <item>
      <title>Progressive Architecture in Go: A Smooth Path from Monolith to Microservices</title>
      <dc:creator>shanjunmei</dc:creator>
      <pubDate>Mon, 10 Aug 2026 03:26:53 +0000</pubDate>
      <link>https://dev.to/shanjunmei/progressive-architecture-in-go-a-smooth-path-from-monolith-to-microservices-41l5</link>
      <guid>https://dev.to/shanjunmei/progressive-architecture-in-go-a-smooth-path-from-monolith-to-microservices-41l5</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Not a utopia, but a pragmatic strategy: start with the cost-effectiveness of a monolith to validate your business quickly, and later, when traffic grows, split into microservices with near-zero migration cost. This article explores how to achieve such capability in Go projects using a compile‑time dependency injection tool that promotes a unified service entry interface.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  1. The Common Architectural Dilemma
&lt;/h2&gt;

&lt;p&gt;Most Go backend teams have faced this dilemma:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In the early stage&lt;/strong&gt;, microservices sound great—independent deployment, independent scaling, technological heterogeneity. But the reality is that maintaining multiple independent processes, setting up service discovery, building distributed tracing, and handling cross-service debugging come with a heavy infrastructure cost, especially when the business model is still unproven.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;As the business grows&lt;/strong&gt;, the monolith starts to show its pain points: a specific module needs independent scaling due to traffic spikes, but the whole application must be redeployed together; a change in one place affects the entire system, increasing the regression testing cost; as the team grows, merge conflicts in a shared codebase become frequent.&lt;/p&gt;

&lt;p&gt;Teams often fall into an awkward cycle: choosing monolith for cost reasons early on, and later rebuilding into microservices at a high cost—often much higher than expected.&lt;/p&gt;

&lt;p&gt;The core question is: &lt;strong&gt;Is there an architectural approach that allows us to run at very low cost in the early stage, yet transition to microservices with minimal effort when needed?&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Core Ideas: Unified Interface + Modular Organisation
&lt;/h2&gt;

&lt;p&gt;The key to enabling progressive architecture lies in two design principles:&lt;/p&gt;

&lt;h3&gt;
  
  
  2.1 Each Service Exposes a Uniform Executable Interface
&lt;/h3&gt;

&lt;p&gt;If every service’s startup entry is a standard function—say, &lt;code&gt;func(context.Context) error&lt;/code&gt;—then:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For merged deployment, simply run these functions concurrently in &lt;code&gt;main&lt;/code&gt; using &lt;code&gt;errgroup&lt;/code&gt; or goroutines.&lt;/li&gt;
&lt;li&gt;For standalone deployment, each service just calls its own function in its own &lt;code&gt;main&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The code path is identical; there is no difference.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2.2 Each Service Has Its Own Isolated Dependency Graph
&lt;/h3&gt;

&lt;p&gt;Each service should manage its own dependencies independently, without sharing the dependency graph with others. That way:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When merged, dependencies do not interfere with each other.&lt;/li&gt;
&lt;li&gt;When split, each service can run on its own without external adjustments.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  3. Using a Compile‑time DI Tool to Enforce the Interface
&lt;/h2&gt;

&lt;p&gt;To conveniently build each service with its own isolated dependency graph and expose the same &lt;code&gt;func(context.Context) error&lt;/code&gt; signature, we can use a compile‑time dependency injection tool. The tool described here is &lt;strong&gt;not&lt;/strong&gt; Uber’s &lt;code&gt;go.uber.org/dig&lt;/code&gt; (a runtime‑reflection library); it is an independent implementation that generates code at compile time, resulting in zero runtime overhead.&lt;/p&gt;

&lt;p&gt;With this tool, each service module defines its providers and invokes its startup logic inside a &lt;code&gt;dig.Build&lt;/code&gt; call, which returns exactly the desired function type:&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;// user/di.go&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;InitUserService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cfg&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;UserConfig&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;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&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;dig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;dig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Supply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;dig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Provide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NewUserRepo&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;dig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Provide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NewUserHandler&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;dig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Invoke&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;h&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;UserHandler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Start&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="c"&gt;// order/di.go&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;InitOrderService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cfg&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;OrderConfig&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;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&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;dig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;dig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Supply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;dig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Provide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NewOrderRepo&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;dig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Provide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NewOrderHandler&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;dig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Invoke&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;h&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;OrderHandler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key points:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;dig.Build&lt;/code&gt; returns a pure Go function, not a framework object.&lt;/li&gt;
&lt;li&gt;This function signature matches standard library patterns (&lt;code&gt;errgroup.Go&lt;/code&gt;, &lt;code&gt;http.Server&lt;/code&gt;, etc.).&lt;/li&gt;
&lt;li&gt;Each service’s dependency graph is fully encapsulated and does not interfere with others.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The generated code has &lt;strong&gt;zero runtime reflection&lt;/strong&gt; and &lt;strong&gt;zero runtime dependency&lt;/strong&gt;, with performance identical to manually written initialisation.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. The Evolutionary Path
&lt;/h2&gt;

&lt;p&gt;With the foundation above, the migration path becomes straightforward.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 1: Early stage – Merged Deployment
&lt;/h3&gt;

&lt;p&gt;All services run in a single process:&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;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;eg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;errgroup&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithContext&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;span class="n"&gt;eg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Go&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;InitUserService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userCfg&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;eg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Go&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;InitOrderService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;orderCfg&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;eg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Go&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;InitPaymentService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;paymentCfg&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="c"&gt;// more services...&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;eg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Wait&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Cost&lt;/strong&gt;: managing 1 binary, 1 process, 1 set of logs.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Benefits&lt;/strong&gt;: easy development and debugging, zero‑latency cross‑service calls (in‑process function calls).&lt;/p&gt;
&lt;h3&gt;
  
  
  Phase 2: Business growth – Gradual Split
&lt;/h3&gt;

&lt;p&gt;When a particular service (e.g., order service) becomes a hotspot and needs independent scaling:&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;// order-service/main.go – standalone deployment&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="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;InitOrderService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;orderCfg&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;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Core business logic – zero changes&lt;/strong&gt;. Other services remain merged.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 3: Maturity – Full Microservices
&lt;/h3&gt;

&lt;p&gt;Every service runs independently, with service discovery, API Gateway, and other infrastructure in place.&lt;/p&gt;

&lt;p&gt;Notice: &lt;strong&gt;every &lt;code&gt;InitXxxService&lt;/code&gt; function has the same signature from day one&lt;/strong&gt;. From the very beginning, the code is already structured for microservices—you just chose to run them together in one process first.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Merged deployment and microservice deployment share the exact same code path. The former is not a compromised version; it is a subset of the latter.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Important Constraint: Configuration Injection Inside Modules
&lt;/h2&gt;

&lt;p&gt;When organising modules, one critical limitation must be observed: &lt;strong&gt;inside a &lt;code&gt;dig.Module&lt;/code&gt;, you cannot use &lt;code&gt;dig.Supply&lt;/code&gt; with function parameters&lt;/strong&gt; (runtime values), because the code generator runs at compile time and cannot capture those values.&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;// ❌ Incorrect: Supplying a function parameter inside Module&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;UserServiceModule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cfg&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;UserConfig&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;dig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Option&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;dig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;dig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Supply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;   &lt;span class="c"&gt;// compile error: cfg is a function parameter, invisible at generation time&lt;/span&gt;
        &lt;span class="n"&gt;dig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Provide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NewUserRepo&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="c"&gt;// ✅ Correct: Module only organises providers; configuration is supplied at Build level&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;UserServiceModule&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;dig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Option&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;dig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;dig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Provide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NewUserRepo&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;dig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Provide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NewUserHandler&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="c"&gt;// do NOT Supply configuration here&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;InitUserService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cfg&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;UserConfig&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;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&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;dig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;dig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Supply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;        &lt;span class="c"&gt;// supply configuration at Build level&lt;/span&gt;
        &lt;span class="n"&gt;UserServiceModule&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;This limitation is inherent to compile‑time code generation: all dependencies are resolved during &lt;code&gt;go generate&lt;/code&gt;, while function parameters are only known at runtime, so the generator cannot anticipate them.&lt;/p&gt;

&lt;p&gt;This design choice also brings a significant benefit—&lt;strong&gt;generated code has zero runtime reflection and zero runtime dependency&lt;/strong&gt;, with performance identical to manually written initialisation.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Conclusion
&lt;/h2&gt;

&lt;p&gt;Progressive architecture is not about a specific deployment pattern; it is about &lt;strong&gt;architectural flexibility&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Start with a monolith&lt;/strong&gt; to keep costs low and validate business quickly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Evolve into microservices&lt;/strong&gt; as needed—scale independently and evolve autonomously.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Throughout the process, the code path remains consistent&lt;/strong&gt;—no “special code for monolith” and no costly rewrites for splitting.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the Go ecosystem, the key to such flexibility is having a DI tool that provides a &lt;strong&gt;unified executable interface&lt;/strong&gt; and &lt;strong&gt;independent, composable dependency graphs&lt;/strong&gt;. The compile‑time approach described here returns a standard &lt;code&gt;func(context.Context) error&lt;/code&gt;—which aligns perfectly with Go's philosophy of preferring simple functions and composition over framework‑bound objects.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;The &lt;code&gt;dig&lt;/code&gt; tool mentioned in this article is a compile‑time dependency injection implementation **independent of Uber's &lt;code&gt;go.uber.org/dig&lt;/code&gt;&lt;/em&gt;&lt;em&gt;. It focuses on code generation and zero‑runtime overhead, making it suitable for building progressive architecture as described above.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>backend</category>
      <category>go</category>
      <category>microservices</category>
    </item>
    <item>
      <title>Ditching Wire &amp; Fx: I Built a Truly Go-Style Compile-Time DI Library</title>
      <dc:creator>shanjunmei</dc:creator>
      <pubDate>Wed, 08 Jul 2026 13:29:22 +0000</pubDate>
      <link>https://dev.to/shanjunmei/ditching-wire-fx-i-built-a-truly-go-style-compile-time-di-library-2455</link>
      <guid>https://dev.to/shanjunmei/ditching-wire-fx-i-built-a-truly-go-style-compile-time-di-library-2455</guid>
      <description>&lt;p&gt;After years writing Go, I’ve clung to a core conviction:&lt;br&gt;
Engineering optimization in Go shouldn’t be a series of compromising tradeoffs — it ought to be all-gain evolution.&lt;/p&gt;

&lt;p&gt;This rings especially true for Dependency Injection (DI), the foundational engineering pillar for mid-to-large Go codebases. The two dominant mainstream DI solutions in the ecosystem force developers into an unavoidable binary compromise:&lt;/p&gt;
&lt;h2&gt;
  
  
  ✅ Uber Fx
&lt;/h2&gt;

&lt;p&gt;Boasts clean, elegant APIs and powerful modularization with a gentle learning curve, yet it relies heavily on runtime reflection.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Slow startup performance&lt;/li&gt;
&lt;li&gt;Hidden runtime performance overhead in production&lt;/li&gt;
&lt;li&gt;Missing dependencies, type mismatches and circular dependencies only surface via panics at launch time&lt;/li&gt;
&lt;li&gt;Larger binary footprints
It’s simply not viable for high-throughput services and foundational infrastructure components.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  ✅ Google Wire
&lt;/h2&gt;

&lt;p&gt;Delivers compile-time code generation, zero reflection and zero runtime overhead — safe, performant, and fully aligned with Go’s static compilation philosophy.&lt;br&gt;
Yet its developer experience is painfully counterintuitive:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Verbose, boilerplate-heavy syntax&lt;/li&gt;
&lt;li&gt;Mandatory repetitive &lt;code&gt;wire.NewSet&lt;/code&gt; declarations&lt;/li&gt;
&lt;li&gt;Clunky empty return placeholder patterns&lt;/li&gt;
&lt;li&gt;Severe restrictions on value injection (only constants supported natively)&lt;/li&gt;
&lt;li&gt;No native nested module support, leading to messy structure as projects scale&lt;/li&gt;
&lt;li&gt;No built-in Invoke hooks for post-initialization startup logic&lt;/li&gt;
&lt;li&gt;Cryptic error reporting that makes dependency debugging a slog&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a long time, every project I worked on left me stuck between two bad choices:&lt;br&gt;
Pick great DX and sacrifice runtime performance &amp;amp; stability; pick performance and safety while enduring a terrible developer workflow.&lt;/p&gt;

&lt;p&gt;Since no existing tool hits every mark, I built my own.&lt;/p&gt;

&lt;p&gt;Meet &lt;strong&gt;dig&lt;/strong&gt; — a compile-time DI library that merges Fx’s ergonomic API with Wire’s compile-time safety, engineered from the ground up to fit Go’s design ethos.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why Mainstream DI Libraries Are All Flawed
&lt;/h2&gt;

&lt;p&gt;Many developers argue small Go projects don’t need DI; manual initialization works fine for trivial codebases.&lt;br&gt;
But once you scale to multi-module layered architectures, microservices or monorepos, handwritten initialization becomes unmaintainable chaos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unmanaged tangled dependency graphs&lt;/li&gt;
&lt;li&gt;Rampant global variables&lt;/li&gt;
&lt;li&gt;Dispersed initialization logic scattered across packages&lt;/li&gt;
&lt;li&gt;Tiny breaking changes triggering cascading failures&lt;/li&gt;
&lt;li&gt;Near-impossible mocking for unit tests&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;DI is a non-negotiable necessity at scale — yet the two leading options carry fatal downsides:&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Fx’s Critical Flaw: Runtime Uncertainty From Reflection
&lt;/h3&gt;

&lt;p&gt;All dependency resolution and module composition in Fx happens via runtime reflection.&lt;br&gt;
This means missing dependencies, circular references and type mismatches pass compilation silently, only crashing your service in production on startup.&lt;br&gt;
For backend services and core infrastructure, this risk is completely unacceptable. On top of that, persistent reflection overhead slows service boot times and bloats binaries — directly violating Go’s core tenets of simplicity, efficiency and predictability.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Wire’s Critical Flaw: Maximum Performance, Minimal Developer Experience
&lt;/h3&gt;

&lt;p&gt;Wire is Google’s official tooling with compile-time code generation, zero reflection and zero runtime dependencies, maximizing safety.&lt;br&gt;
Its design, however, works against developer productivity:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mandates maintaining dozens of repetitive &lt;code&gt;wire.NewSet&lt;/code&gt; provider sets, creating massive boilerplate&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;wire.Value&lt;/code&gt; only accepts compile-time constants; runtime dynamic values cannot be injected&lt;/li&gt;
&lt;li&gt;Modules can only be composed flatly, with no nested layering support — large repos devolve into disorganized messes&lt;/li&gt;
&lt;li&gt;No native Invoke startup hooks; post-init logic requires handwritten wrappers&lt;/li&gt;
&lt;li&gt;Obscure error messages that drastically slow down dependency troubleshooting&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To sum up: Fx wins on developer experience but loses performance and safety; Wire delivers peak performance and safety at the cost of a painful workflow.&lt;br&gt;
I set out to build something that delivers everything at once.&lt;/p&gt;
&lt;h2&gt;
  
  
  Core Mission of dig: Filling the Final Gap in Go’s DI Ecosystem
&lt;/h2&gt;

&lt;p&gt;dig was built with a single, clear objective: avoid redundant reinvention, and deliver the optimal solution free of compromises.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Retain Wire’s core strengths: compile-time resolution, zero reflection, no runtime library dependencies&lt;/li&gt;
&lt;li&gt;Match Fx’s developer experience: minimal, readable APIs and native modularization&lt;/li&gt;
&lt;li&gt;Add missing capabilities absent from both libraries: enforced closure safety validation, full observability, flexible arbitrary value injection, native generics support, and granular unused component policies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The end result: compile-time safety guarantees, streamlined developer experience, and zero production runtime overhead.&lt;/p&gt;
&lt;h2&gt;
  
  
  Core Advantages of dig: Solving Every Pain Point of Traditional DI
&lt;/h2&gt;
&lt;h3&gt;
  
  
  1. Dual Safety Guarantees: Compile-Time Validation + Zero Runtime Cost
&lt;/h3&gt;

&lt;p&gt;All dependency graph parsing, circular dependency detection and unused component validation run entirely during &lt;code&gt;go generate&lt;/code&gt;.&lt;br&gt;
dig outputs pure vanilla Go source code — no reflection, no proprietary runtime framework, and zero runtime dependency on the dig library itself.&lt;br&gt;
The final binary matches the performance and startup speed of fully handwritten initialization logic, outperforming Fx by a wide margin.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Fx-Style Minimal API, Eliminating Wire’s Verbose Boilerplate
&lt;/h3&gt;

&lt;p&gt;dig exposes only five core APIs with zero learning curve, each with clear, idiomatic Go semantics:&lt;br&gt;
&lt;code&gt;Build / Provide / Supply / Invoke / Module&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Provide&lt;/code&gt;: Register constructor functions&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Supply&lt;/code&gt;: Inject arbitrary values (constants, package-scoped variables, runtime dynamic values) — removes Wire’s constant-only injection limitation&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Invoke&lt;/code&gt;: Execute startup logic once all dependencies are fully resolved&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Module&lt;/code&gt;: Enable layered, nested modular composition&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A minimal example to demonstrate its elegance:&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 digen&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;InitApp&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;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&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;dig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;dig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Provide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NewConfig&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;dig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Provide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NewDB&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;dig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Supply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DefaultTimeout&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;dig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Invoke&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;srv&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;srv&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="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;h3&gt;
  
  
  3. Exclusive Closure Safety Mechanism to Eliminate Silent Bugs
&lt;/h3&gt;

&lt;p&gt;Both Wire and Fx suffer from unvalidated closure capture risks, which frequently trigger elusive production runtime failures.&lt;br&gt;
dig’s dedicated compiler enforces strict closure validation: capturing local variables defined inside the injector function &lt;code&gt;InitApp&lt;/code&gt; is forbidden. Only package-level variables and literal values are permitted. This hard guard eliminates entire classes of silent hidden dependency bugs — a safety feature unique to dig among all mainstream Go DI libraries.&lt;/p&gt;
&lt;h3&gt;
  
  
  4. Full Observability Suite Beyond Basic Debug Logging
&lt;/h3&gt;

&lt;p&gt;dig goes far beyond simple &lt;code&gt;-debug&lt;/code&gt; print statements with end-to-end observability:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Compile-time: Three-tier unused component policies (&lt;code&gt;error&lt;/code&gt;/&lt;code&gt;ignore&lt;/code&gt;/&lt;code&gt;drop&lt;/code&gt;) + package alias optimization to prune dead code upfront&lt;/li&gt;
&lt;li&gt;Runtime: Global hookable &lt;code&gt;Logf&lt;/code&gt; interface, seamlessly integrating with structured loggers like zap and logrus&lt;/li&gt;
&lt;li&gt;Errors include full module hierarchy and complete dependency chains for pinpoint debugging, replacing opaque error outputs&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  5. Modern Go Engineering Support: Generics, Modularization &amp;amp; Conditional Logic
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Native support for generic constructors and generic dependency injection, no manual wrapping required&lt;/li&gt;
&lt;li&gt;Nested module layering built-in, perfectly suited for large monorepo multi-module architectures&lt;/li&gt;
&lt;li&gt;Combined runtime conditional branching + compile build tag selection for maximum flexibility&lt;/li&gt;
&lt;li&gt;Wrapper type utilities to resolve primitive type injection conflicts, eliminating identical-type dependency ambiguity errors&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Side-by-Side Comparison Against Third-Party DI Tools
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Capability&lt;/th&gt;
&lt;th&gt;dig&lt;/th&gt;
&lt;th&gt;Google Wire&lt;/th&gt;
&lt;th&gt;Uber Fx&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Implementation Approach&lt;/td&gt;
&lt;td&gt;Compile-time code generation&lt;/td&gt;
&lt;td&gt;Compile-time code generation&lt;/td&gt;
&lt;td&gt;Runtime reflection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Zero runtime reflection&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Zero runtime library dependencies&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compile-time error interception&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌ (Panics at runtime)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Minimal Fx-style clean API&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌ (Verbose boilerplate)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mandatory closure safety validation&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Flexible arbitrary value injection&lt;/td&gt;
&lt;td&gt;✅ (All value types)&lt;/td&gt;
&lt;td&gt;❌ (Constants only)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Granular unused component policies&lt;/td&gt;
&lt;td&gt;✅ (3 configurable modes)&lt;/td&gt;
&lt;td&gt;❌ (Only deletion)&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h2&gt;
  
  
  My Thoughts: What Go DI Should Truly Look Like
&lt;/h2&gt;

&lt;p&gt;Many developers resist DI in Go — their aversion stems from heavy Java-style frameworks, invasive abstractions and opaque runtime magic.&lt;br&gt;
Go’s design philosophy centers on simplicity, transparency, predictability and zero hidden behavior:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fx violates predictability with opaque runtime reflection magic&lt;/li&gt;
&lt;li&gt;Wire violates simplicity with verbose, friction-heavy syntax&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;dig exists to return to Go’s foundational principles:&lt;br&gt;
No runtime black-box magic; all behavior fully transparent and traceable. No redundant boilerplate; every design decision prioritizes developer efficiency.&lt;br&gt;
It is not built for the sake of reinventing wheels, but to resolve real-world production pain points:&lt;br&gt;
Lightweight for small projects, structured and controllable for massive monorepos — striking a perfect balance between performance, safety and developer experience.&lt;/p&gt;
&lt;h2&gt;
  
  
  Quick Integration &amp;amp; Open Source Repository
&lt;/h2&gt;

&lt;p&gt;Stable Version: v1.0.10&lt;br&gt;
Minimum Go Version: Go 1.21+&lt;br&gt;
License: MIT (Free for commercial use)&lt;/p&gt;

&lt;p&gt;Installation Commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go get github.com/shanjunmei/dig@v1.0.10
go &lt;span class="nb"&gt;install &lt;/span&gt;github.com/shanjunmei/dig/cmd/digen@latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🔥 GitHub Repository: &lt;a href="https://github.com/shanjunmei/dig" rel="noopener noreferrer"&gt;https://github.com/shanjunmei/dig&lt;/a&gt;&lt;br&gt;
Comprehensive documentation, sample projects, migration guides and advanced usage examples are all available, with active ongoing maintenance. Star, file Issues or submit PRs to join the discussion!&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing Remarks
&lt;/h2&gt;

&lt;p&gt;There is no universally perfect technical choice, but there are options that align far better with a language’s core philosophy.&lt;br&gt;
dig was not created to replace Wire or Fx — it offers Go developers a new, zero-compromise, universally adaptable DI alternative:&lt;br&gt;
It delivers Wire’s extreme performance and compile-time safety, paired with Fx’s elegant developer experience and modular power, plus exclusive built-in safety validations and full observability tooling.&lt;/p&gt;

&lt;p&gt;If you’ve grown frustrated with Fx’s reflection overhead or Wire’s clunky syntax, give dig a try to experience dependency injection truly built for Go’s design language.&lt;/p&gt;

&lt;p&gt;Feel free to test the library, share feedback, submit feature requests, and collaborate to refine a DI tool tailored to the Go ecosystem!&lt;/p&gt;

</description>
      <category>cli</category>
      <category>go</category>
      <category>architecture</category>
      <category>developer</category>
    </item>
    <item>
      <title>What should a modern Go dependency injection framework look like?</title>
      <dc:creator>shanjunmei</dc:creator>
      <pubDate>Wed, 08 Jul 2026 06:11:58 +0000</pubDate>
      <link>https://dev.to/shanjunmei/what-should-a-modern-go-dependency-injection-framework-look-like-fg7</link>
      <guid>https://dev.to/shanjunmei/what-should-a-modern-go-dependency-injection-framework-look-like-fg7</guid>
      <description>&lt;p&gt;Go DI has always been a trade-off.&lt;/p&gt;

&lt;p&gt;Runtime DI frameworks provide a convenient API, but they rely on reflection and discover dependency problems during application startup.&lt;/p&gt;

&lt;p&gt;Compile-time DI frameworks avoid those problems, but some APIs become verbose and less ergonomic.&lt;/p&gt;

&lt;p&gt;I have been exploring a different approach:&lt;/p&gt;

&lt;p&gt;dig — compile-time dependency injection for Go with an Fx-style API and Wire-style code generation.&lt;/p&gt;

&lt;p&gt;Repository:&lt;br&gt;
&lt;a href="https://github.com/shanjunmei/dig" rel="noopener noreferrer"&gt;https://github.com/shanjunmei/dig&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The main design goals are:&lt;/p&gt;

&lt;p&gt;dependency graph resolution during go generate&lt;br&gt;
zero runtime reflection&lt;br&gt;
zero runtime dependency after code generation&lt;br&gt;
generated code is plain Go&lt;br&gt;
minimal API: &lt;code&gt;dig.Build&lt;/code&gt;, &lt;code&gt;dig.Provide&lt;/code&gt;, &lt;code&gt;dig.Supply&lt;/code&gt;, &lt;code&gt;dig.Invoke&lt;/code&gt;, &lt;code&gt;dig.Module&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Example:&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;InitApp&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;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&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;dig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;dig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Provide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NewConfig&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;dig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Provide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NewDatabase&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;dig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;StartServer&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;After generation, the application does not depend on a DI runtime.&lt;/p&gt;

&lt;p&gt;Some design questions I would like feedback on:&lt;/p&gt;

&lt;p&gt;Do you prefer compile-time DI or runtime DI in large Go projects?&lt;br&gt;
Is avoiding reflection an important advantage in practice?&lt;br&gt;
Are there DI patterns from Wire/Fx/custom solutions that should be preserved?&lt;br&gt;
What API choices would make a Go DI library feel natural?&lt;/p&gt;

&lt;p&gt;The goal is not to create another DI library for its own sake, but to explore what a more Go-like DI design could look like.&lt;/p&gt;

&lt;p&gt;Feedback and criticism are welcome.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>go</category>
      <category>opensource</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
