<?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: Tetsuro Mikami</title>
    <description>The latest articles on DEV Community by Tetsuro Mikami (@mickamy).</description>
    <link>https://dev.to/mickamy</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%2F718743%2F5857c080-40ea-42de-a60f-32b5841f7f5f.jpeg</url>
      <title>DEV Community: Tetsuro Mikami</title>
      <link>https://dev.to/mickamy</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mickamy"/>
    <language>en</language>
    <item>
      <title>I built five code generators to delete Go boilerplate</title>
      <dc:creator>Tetsuro Mikami</dc:creator>
      <pubDate>Mon, 17 Aug 2026 03:43:22 +0000</pubDate>
      <link>https://dev.to/mickamy/i-built-five-code-generators-to-delete-go-boilerplate-h65</link>
      <guid>https://dev.to/mickamy/i-built-five-code-generators-to-delete-go-boilerplate-h65</guid>
      <description>&lt;p&gt;Writing Go means writing the same mechanical code over and over: constructor wiring, conversions between domain types and API types, test fixtures, row scanning, message localization.&lt;/p&gt;

&lt;p&gt;There are plenty of libraries that erase this with runtime reflection — but that moves failure to runtime and hides the behavior inside the library. So I went the other way: &lt;strong&gt;generate the code you were going to write anyway, and commit it to the repository.&lt;/strong&gt;&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/go-kanna" rel="noopener noreferrer"&gt;
        go-kanna
      &lt;/a&gt; / &lt;a href="https://github.com/go-kanna/kanna" rel="noopener noreferrer"&gt;
        kanna
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Go code generators built on the standard library — DI, struct mapping, test fixtures, ORM. Each works on its own and emits plain Go with no runtime reflection.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;kanna&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a href="https://github.com/go-kanna/kanna/actions/workflows/ci.yaml" rel="noopener noreferrer"&gt;&lt;img src="https://github.com/go-kanna/kanna/actions/workflows/ci.yaml/badge.svg" alt="CI"&gt;&lt;/a&gt;
&lt;a href="https://codecov.io/gh/go-kanna/kanna" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/10e62ead46ad651b7f1bd20948d85dc15f201cc2e0352db9673ca35775f6e303/68747470733a2f2f636f6465636f762e696f2f67682f676f2d6b616e6e612f6b616e6e612f67726170682f62616467652e737667" alt="codecov"&gt;&lt;/a&gt;
&lt;a href="https://pkg.go.dev/github.com/go-kanna/kanna" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/be2c613139f8a1daee6705c3e228f9a7bf8b61ab134aa14e7ddaa64a9a76efe4/68747470733a2f2f706b672e676f2e6465762f62616467652f6769746875622e636f6d2f676f2d6b616e6e612f6b616e6e612e737667" alt="Go Reference"&gt;&lt;/a&gt;
&lt;a href="https://github.com/go-kanna/kanna/LICENSE" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/08cef40a9105b6526ca22088bc514fbfdbc9aac1ddbf8d4e6c750e3a88a44dca/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4d49542d626c75652e737667" alt="License: MIT"&gt;&lt;/a&gt;
&lt;a href="https://github.com/go-kanna/kanna/tags" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/1ce23c0195c1bd655c57640f1747315137f3a90f63c7d2f6d793a057debdb49c/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f762f7461672f676f2d6b616e6e612f6b616e6e613f6c6162656c3d72656c6561736526736f72743d73656d766572" alt="Release"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Go code generators built on the standard library — DI, struct mapping, test fixtures, ORM, i18n. Each
works on its own and emits plain Go with no runtime reflection.&lt;/p&gt;
&lt;p&gt;Your structs are the source of truth. Point a generator at a package and it writes the code you would otherwise write by
hand, so the output stays readable, debuggable, and free of anything to learn at runtime.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;kanna-di&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;Wires a container struct from the providers it can find, and writes a plain constructor.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Install&lt;/h3&gt;
&lt;/div&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;go get -tool github.com/go-kanna/kanna/cmd/kanna-di&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Use&lt;/h3&gt;

&lt;/div&gt;
&lt;p&gt;A &lt;strong&gt;provider&lt;/strong&gt; is a top-level function whose first result is a named type, a pointer to one, or an interface, optionally
followed by an &lt;code&gt;error&lt;/code&gt;. Nothing needs to be registered — kanna-di finds them by scanning the packages you point it at.&lt;/p&gt;
&lt;p&gt;A &lt;strong&gt;container&lt;/strong&gt; is a struct whose fields carry a &lt;code&gt;di&lt;/code&gt; tag.&lt;/p&gt;
&lt;div class="highlight highlight-source-go notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;package&lt;/span&gt; app
&lt;span class="pl-c"&gt;//go:generate go tool kanna-di&lt;/span&gt;&lt;/pre&gt;…
&lt;/div&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/go-kanna/kanna" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;The output is ordinary Go. No reflection, no round-trips through &lt;code&gt;interface{}&lt;/code&gt; — it shows up in code review and &lt;code&gt;git diff&lt;/code&gt;, and you can step through it in a debugger.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;kanna-di&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;generates constructor dependency wiring&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;kanna-fixture&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;generates test fixtures with every field filled&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;kanna-mapper&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;generates domain ↔ wire type conversion functions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;kanna-orm&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;generates type-safe queries, row scanning, and relations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;kanna-i18n&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;generates typed message constructors with translations embedded&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;All five install as independent &lt;code&gt;go tool&lt;/code&gt;s, so you can use just one. The input is the same for all of them: &lt;strong&gt;the struct declarations and struct tags you already have.&lt;/strong&gt; Nothing gets registered anywhere.&lt;/p&gt;

&lt;p&gt;Here is what each one does, shown with actual generated output.&lt;/p&gt;

&lt;h2&gt;
  
  
  kanna-di — constructor wiring
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What it does:&lt;/strong&gt; looks at a container struct's fields, finds the functions that can build them, and writes a constructor that calls them in dependency order.&lt;/p&gt;

&lt;p&gt;A provider is "a package-level function whose first return value is a named type, a pointer to one, or an interface" (a trailing &lt;code&gt;error&lt;/code&gt; is fine). &lt;strong&gt;No registration — it scans the packages you point it at.&lt;/strong&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="c"&gt;// app/app.go&lt;/span&gt;
&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;

&lt;span class="c"&gt;//go:generate go tool kanna-di ./...&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;DB&lt;/span&gt; &lt;span class="k"&gt;struct&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;NewDB&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;DB&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;DB&lt;/span&gt;&lt;span class="p"&gt;{}&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;User&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;db&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;DB&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;NewUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;DB&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;User&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;User&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;}&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;Container&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;User&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt; &lt;span class="s"&gt;`di:""`&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;go generate ./...&lt;/code&gt; writes &lt;code&gt;di_gen.go&lt;/code&gt; next to it:&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;// app/di_gen.go&lt;/span&gt;
&lt;span class="c"&gt;// Code generated by kanna. DO NOT EDIT.&lt;/span&gt;

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

&lt;span class="c"&gt;// NewContainer initializes dependencies and constructs Container.&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;NewContainer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Container&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&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;user&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;NewUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&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;Container&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;user&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;If anything in the dependency chain returns an &lt;code&gt;error&lt;/code&gt;, the constructor returns one too and propagates it.&lt;/p&gt;

&lt;p&gt;Whatever cannot be inferred, you spell out with tags on the container struct:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tag&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;di:""&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;resolve from whichever provider returns the field's type&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;di:"with=NewReadOnlyDB"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;name the provider to use&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;di:"arg"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;make the dependency a constructor parameter&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;di:"returns"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;make that type the constructor's return type (an interface works)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;di:"embed"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;take a struct as a parameter and offer its exported fields as resolution sources&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A &lt;code&gt;//kanna:container&lt;/code&gt; comment can also set the constructor name, the return type, and whether a &lt;code&gt;MustNew*&lt;/code&gt; variant is generated.&lt;/p&gt;

&lt;p&gt;When several providers match, it does not pick one silently — you get &lt;strong&gt;an error with the field's position and the list of candidates&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  kanna-fixture — test fixtures
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What it does:&lt;/strong&gt; writes one constructor per struct in a package, with every field already filled. Tests state only the values they care about.&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;// model/user.go&lt;/span&gt;
&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;

&lt;span class="c"&gt;//go:generate go tool kanna-fixture -source ./model -destination ./fixture&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;User&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;Name&lt;/span&gt;  &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="n"&gt;Email&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="n"&gt;Age&lt;/span&gt;   &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="s"&gt;`fake:"{number:18,65}"`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// fixture/fixture_gen.go&lt;/span&gt;
&lt;span class="c"&gt;// Code generated by kanna. DO NOT EDIT.&lt;/span&gt;

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

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;setters&lt;/span&gt; &lt;span class="o"&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;m&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;User&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="n"&gt;gofakeit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Int64&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="n"&gt;gofakeit&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="n"&gt;Email&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;gofakeit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Email&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;Age&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;   &lt;span class="n"&gt;gofakeit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;65&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;s&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;setters&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;s&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;m&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="n"&gt;m&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using it:&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;u&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;fixture&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;User&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;m&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"known@example.com"&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;Every exported struct is covered — there is nothing to opt into.&lt;/strong&gt; That is what keeps fixtures from falling behind the model. Field names drive the inference, so &lt;code&gt;Email&lt;/code&gt; gets &lt;code&gt;gofakeit.Email()&lt;/code&gt; and &lt;code&gt;Name&lt;/code&gt; gets &lt;code&gt;gofakeit.Name()&lt;/code&gt;; a tag like &lt;code&gt;fake:"{number:18,65}"&lt;/code&gt; overrides it.&lt;/p&gt;

&lt;p&gt;Types that need more than gofakeit, like &lt;code&gt;uuid.UUID&lt;/code&gt;, are handled too — and if the destination module does not require the package yet, the generator says so.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Generation is deterministic; the values are not. A test that needs the same data twice should call &lt;code&gt;gofakeit.Seed(n)&lt;/code&gt; in &lt;code&gt;TestMain&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  kanna-mapper — domain ↔ wire conversions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What it does:&lt;/strong&gt; writes the conversion functions between types you own and types you don't (protobuf output, DTOs). For fields Go cannot convert by itself, it calls a converter you registered once.&lt;/p&gt;

&lt;p&gt;Register the conversions one time:&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;// lib/converters/converters.go&lt;/span&gt;
&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;converters&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s"&gt;"github.com/go-kanna/kanna/mapper"&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;mapper&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;UUIDToString&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// uuid.UUID → string&lt;/span&gt;
    &lt;span class="n"&gt;mapper&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RegisterE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Parse&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c"&gt;// string → uuid.UUID; this one can fail&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then name the pairs to map:&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;// mapper/gen.go&lt;/span&gt;
&lt;span class="c"&gt;//go:generate go tool kanna-mapper -types=model.Employee:*employeev1.Employee -converters=../lib/converters&lt;/span&gt;
&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;mapper&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The generated functions:&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;// mapper/mapper_gen.go&lt;/span&gt;
&lt;span class="c"&gt;// EmployeeToEmployeev1 maps model.Employee to *employeev1.Employee.&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;EmployeeToEmployeev1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Employee&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;employeev1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Employee&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;employeev1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Employee&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="n"&gt;converters&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UUIDToString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="n"&gt;src&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="n"&gt;Address&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;AddressToEmployeev1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Address&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;// EmployeeFromEmployeev1 maps *employeev1.Employee to model.Employee.&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;EmployeeFromEmployeev1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;employeev1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Employee&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Employee&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="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;src&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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Employee&lt;/span&gt;&lt;span class="p"&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;v1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err0&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetId&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;err0&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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Employee&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;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"map model.Employee.ID: %w"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What you get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Both directions.&lt;/strong&gt; &lt;code&gt;To&lt;/code&gt; and &lt;code&gt;From&lt;/code&gt; are generated together&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nested structs are walked recursively&lt;/strong&gt; (&lt;code&gt;AddressToEmployeev1&lt;/code&gt; gets called for you)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error handling for fallible conversions&lt;/strong&gt;, with the failing field named in the message&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;nil&lt;/code&gt; pointer handling&lt;/strong&gt;, returning the zero value&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Protobuf getters&lt;/strong&gt;: &lt;code&gt;src.GetId()&lt;/code&gt; is used where it exists&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;map:"-"&lt;/code&gt; excludes a field, &lt;code&gt;map:"FieldName"&lt;/code&gt; names its counterpart&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  kanna-orm — type-safe queries
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What it does:&lt;/strong&gt; generates per-table query factories, row scanning, relation eager loading, and automatic timestamps from annotated model structs.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;//kanna:table&lt;/code&gt; opts a struct in; everything else is inferred from the fields, and the &lt;code&gt;orm&lt;/code&gt; tag overrides only where the inference disagrees with your schema.&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;// model/model.go&lt;/span&gt;
&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;

&lt;span class="c"&gt;//kanna:table&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;User&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;int&lt;/span&gt;       &lt;span class="c"&gt;// primary key, by name&lt;/span&gt;
    &lt;span class="n"&gt;Name&lt;/span&gt;      &lt;span class="kt"&gt;string&lt;/span&gt;    &lt;span class="c"&gt;// column "name"&lt;/span&gt;
    &lt;span class="n"&gt;Email&lt;/span&gt;     &lt;span class="kt"&gt;string&lt;/span&gt;    &lt;span class="s"&gt;`orm:"email_address"`&lt;/span&gt; &lt;span class="c"&gt;// explicit column name&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="c"&gt;// set automatically on create&lt;/span&gt;
    &lt;span class="n"&gt;Posts&lt;/span&gt;     &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;Post&lt;/span&gt;    &lt;span class="s"&gt;`orm:"has_many,foreign_key:user_id"`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;//kanna:table&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Post&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;int&lt;/span&gt;
    &lt;span class="n"&gt;UserID&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;
    &lt;span class="n"&gt;Title&lt;/span&gt;  &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="n"&gt;User&lt;/span&gt;   &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt; &lt;span class="s"&gt;`orm:"belongs_to,foreign_key:user_id"`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;//go:generate go tool kanna-orm -source ./model -destination ./query&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using it:&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;db&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;orm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sqlDB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;orm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MySQL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// or orm.PostgreSQL&lt;/span&gt;

&lt;span class="n"&gt;users&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="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Users&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"name LIKE ?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"A%"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OrderBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;All&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;posts&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="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Posts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Preload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"User"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;All&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&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="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Transaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&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;tx&lt;/span&gt; &lt;span class="n"&gt;orm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Querier&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;query&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Users&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&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;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;User&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;"Alice"&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;What you get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A factory returning &lt;code&gt;orm.Query[T]&lt;/code&gt;&lt;/strong&gt; per table&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Row scanning&lt;/strong&gt; generated for you (no more lining up &lt;code&gt;rows.Scan&lt;/code&gt; arguments)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Relations&lt;/strong&gt; — &lt;code&gt;has_many&lt;/code&gt; / &lt;code&gt;has_one&lt;/code&gt; / &lt;code&gt;belongs_to&lt;/code&gt; / &lt;code&gt;many_to_many&lt;/code&gt;, eager loading via &lt;code&gt;Preload&lt;/code&gt;, plus &lt;code&gt;Join&lt;/code&gt; / &lt;code&gt;LeftJoin&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Timestamps&lt;/strong&gt; — &lt;code&gt;CreatedAt&lt;/code&gt; / &lt;code&gt;UpdatedAt&lt;/code&gt; are recognized by name and set automatically&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MySQL and PostgreSQL&lt;/strong&gt;, with placeholder and upsert differences absorbed by the runtime&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transactions&lt;/strong&gt; — query code takes an &lt;code&gt;orm.Querier&lt;/code&gt;, so it reads the same inside and outside a transaction&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The query builder, dialects, and transactions live in the &lt;code&gt;orm/&lt;/code&gt; runtime; the generated code is plain Go on top of it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The destination must be a different package from the models. Generating into the model package means one stale output can make the models themselves stop compiling.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  kanna-i18n — typed messages
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What it does:&lt;/strong&gt; generates a typed constructor per message from a directory of locale files — and &lt;strong&gt;embeds the translations into the output&lt;/strong&gt;, so nothing is read or parsed at runtime.&lt;/p&gt;

&lt;p&gt;One file per language, named by its BCP 47 tag:&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="c1"&gt;# locales/en.yaml&lt;/span&gt;
&lt;span class="na"&gt;greeting&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello!"&lt;/span&gt;
&lt;span class="na"&gt;hello&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;{name}!"&lt;/span&gt;
&lt;span class="na"&gt;items_count&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;plural&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;one&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;You&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;have&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;{count}&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;item."&lt;/span&gt;
    &lt;span class="na"&gt;other&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;You&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;have&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;{count}&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;items."&lt;/span&gt;
&lt;span class="na"&gt;total_price&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Total:&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;{price:number}"&lt;/span&gt;
&lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;not_found&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;User&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;not&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;found."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;//go:generate go tool kanna-i18n&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the defaults, that one line reads &lt;code&gt;locales/&lt;/code&gt; and writes &lt;code&gt;messages/i18n_gen.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="c"&gt;// messages/i18n_gen.go&lt;/span&gt;
&lt;span class="c"&gt;// Hello returns the "hello" message.&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;Hello&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="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;i18n&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&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;i18n&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"hello"&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="n"&gt;i18n&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Arg&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;"name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Value&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="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;No setup on the calling side:&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;en&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Localizer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;language&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;English&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="n"&gt;en&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Localize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Hello&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"World"&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What you get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Placeholders become parameters.&lt;/strong&gt; &lt;code&gt;{name}&lt;/code&gt; produces &lt;code&gt;Hello(name string)&lt;/code&gt;; forgetting one is a compile error&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plurals.&lt;/strong&gt; A &lt;code&gt;{count}&lt;/code&gt; parameter following CLDR plural categories — languages like Japanese, which only have &lt;code&gt;other&lt;/code&gt;, are handled correctly&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Locale-aware number formatting.&lt;/strong&gt; &lt;code&gt;{price:number}&lt;/code&gt; renders as &lt;code&gt;1.234,56&lt;/code&gt; in German&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fallback&lt;/strong&gt;, walking &lt;code&gt;en-GB&lt;/code&gt; → &lt;code&gt;en&lt;/code&gt; → the default language&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Missing translations warn; structural mismatches fail.&lt;/strong&gt; A key absent from the default language, a plural shape that differs, a parameter that does not match — those stop generation. A translation that is merely late is a warning, and falls back at runtime&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Since the translations are embedded, there is no YAML to ship with your deploy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install
&lt;/h2&gt;

&lt;p&gt;Take only what you need:&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 &lt;span class="nt"&gt;-tool&lt;/span&gt; github.com/go-kanna/kanna/cmd/kanna-di
go get &lt;span class="nt"&gt;-tool&lt;/span&gt; github.com/go-kanna/kanna/cmd/kanna-fixture
go get &lt;span class="nt"&gt;-tool&lt;/span&gt; github.com/go-kanna/kanna/cmd/kanna-orm

&lt;span class="c"&gt;# the two with a runtime take two lines&lt;/span&gt;
go get &lt;span class="nt"&gt;-tool&lt;/span&gt; github.com/go-kanna/kanna/cmd/kanna-mapper
go get github.com/go-kanna/kanna/mapper

go get &lt;span class="nt"&gt;-tool&lt;/span&gt; github.com/go-kanna/kanna/cmd/kanna-i18n
go get github.com/go-kanna/kanna/i18n
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;mapper&lt;/code&gt; and &lt;code&gt;i18n&lt;/code&gt; take two lines because the parts that depend on runtime values — the converter registry, and CLDR plural rules, number formatting, language fallback — live in a runtime package. The other three are complete with just the generated code.&lt;/p&gt;

&lt;p&gt;Every generator has &lt;code&gt;-check&lt;/code&gt;, so CI can verify the output is not stale:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go tool kanna-di &lt;span class="nt"&gt;-check&lt;/span&gt; ./...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Status
&lt;/h2&gt;

&lt;p&gt;It is v0.0.1: tests and CI are in place, but tag and directive syntax may still move. Go 1.25+, MIT licensed.&lt;/p&gt;

&lt;p&gt;Every generator has a runnable example under &lt;code&gt;examples/&lt;/code&gt; in the repository, and CI regenerates all of them on every run and fails on any diff — so the output you saw above is what the generators actually produce.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/go-kanna/kanna" rel="noopener noreferrer"&gt;https://github.com/go-kanna/kanna&lt;/a&gt;&lt;/p&gt;

</description>
      <category>go</category>
      <category>codepen</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Dependency Injection in Go: How Much Is Enough for Web APIs?</title>
      <dc:creator>Tetsuro Mikami</dc:creator>
      <pubDate>Wed, 24 Dec 2025 06:50:57 +0000</pubDate>
      <link>https://dev.to/mickamy/dependency-injection-in-go-how-much-is-enough-for-web-apis-hp7</link>
      <guid>https://dev.to/mickamy/dependency-injection-in-go-how-much-is-enough-for-web-apis-hp7</guid>
      <description>&lt;p&gt;Dependency Injection (DI) in Go often sparks debates that feel disproportionate to the actual needs of most web APIs. Discussions quickly escalate to containers, lifecycles, scopes, and dynamic resolution—despite the fact that many services have a simple, static dependency graph resolved once at startup.&lt;/p&gt;

&lt;p&gt;This article argues a straightforward position:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;For a typical web API, &lt;strong&gt;compile-time dependency resolution is enough&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;From there, we’ll look at why runtime DI frameworks tend to be overkill, why static approaches like wire already cover most needs, and why a field-tag-based generator can simplify things even further.&lt;/p&gt;




&lt;h2&gt;
  
  
  What most web APIs actually need
&lt;/h2&gt;

&lt;p&gt;If we strip away abstractions, a large percentage of Go web APIs share the same characteristics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stateless request handling&lt;/li&gt;
&lt;li&gt;Dependencies resolved &lt;strong&gt;once at startup&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;A mostly tree-shaped dependency graph&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;config → database → repository → service → handler&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;No runtime rebinding of implementations&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;In this environment, DI is not about flexibility at runtime. It’s about &lt;strong&gt;wiring correctness and maintainability&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Runtime DI frameworks: powerful, but usually unnecessary
&lt;/h2&gt;

&lt;p&gt;Frameworks such as &lt;strong&gt;fx / dig&lt;/strong&gt; or generics-based containers like &lt;strong&gt;do&lt;/strong&gt; shine when your application behaves like a framework itself:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;modules register themselves dynamically&lt;/li&gt;
&lt;li&gt;lifecycle hooks (start/stop) matter&lt;/li&gt;
&lt;li&gt;plugins or optional components are loaded at runtime&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For typical web APIs, however, these features often come with downsides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;additional APIs to learn and remember&lt;/li&gt;
&lt;li&gt;runtime error surfaces for what is fundamentally static wiring&lt;/li&gt;
&lt;li&gt;harder-to-follow control flow during startup&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When dependencies are fixed and known ahead of time, &lt;strong&gt;runtime DI solves a problem you don’t really have&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Static wiring is already enough
&lt;/h2&gt;

&lt;p&gt;If dependencies are static, resolving them at compile time or generation time is a natural fit.&lt;/p&gt;

&lt;p&gt;This is where tools like &lt;strong&gt;wire&lt;/strong&gt; enter the picture. They provide:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;compile-time dependency resolution&lt;/li&gt;
&lt;li&gt;no runtime container&lt;/li&gt;
&lt;li&gt;plain Go code as output&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a normal web API, this model is already sufficient. You get safety, predictability, and zero runtime DI overhead.&lt;/p&gt;

&lt;p&gt;So far, the conclusion is simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;For most web APIs, static DI is enough.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Where wire starts to feel heavy
&lt;/h2&gt;

&lt;p&gt;Wire’s design emphasizes explicitness:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;provider functions&lt;/li&gt;
&lt;li&gt;provider sets&lt;/li&gt;
&lt;li&gt;injector functions per root&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This explicitness can be valuable in some organizational contexts. But in day-to-day API development, it often turns into maintenance overhead:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;provider sets need constant updates as the graph evolves&lt;/li&gt;
&lt;li&gt;DI-specific files grow alongside business code&lt;/li&gt;
&lt;li&gt;the wiring itself becomes something developers have to reason about&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The static model is right—but the &lt;em&gt;amount of ceremony&lt;/em&gt; is not always justified.&lt;/p&gt;




&lt;h2&gt;
  
  
  A lighter static model: field-tag-based generation
&lt;/h2&gt;

&lt;p&gt;If your goal is still static wiring and generated constructors, but with less DI-specific code, a simpler model works well:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;declare what you want in a container struct&lt;/li&gt;
&lt;li&gt;let a generator resolve the graph&lt;/li&gt;
&lt;li&gt;emit plain Go constructors&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Conceptually:&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;type&lt;/span&gt; &lt;span class="n"&gt;Container&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;Handler&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;UserHandler&lt;/span&gt; &lt;span class="s"&gt;`inject:""`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The generated code is just Go:&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;NewContainer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Container&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="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;NewConfig&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;db&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="n"&gt;NewDatabase&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="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="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&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;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;svc&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;NewUserService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&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;NewUserHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;svc&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;Container&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Handler&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No runtime container. No provider sets. No reflection.&lt;/p&gt;

&lt;p&gt;This is the idea behind &lt;strong&gt;injector&lt;/strong&gt;, a field-tag-only DI code generator:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/mickamy/injector" rel="noopener noreferrer"&gt;https://github.com/mickamy/injector&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The link is intentionally placed here, after the argument is established, rather than as an upfront pitch.&lt;/p&gt;




&lt;h2&gt;
  
  
  Is there still a place for wire?
&lt;/h2&gt;

&lt;p&gt;Wire can still make sense when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;provider sets are treated as a &lt;strong&gt;public composition API&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;explicit wiring boundaries are a deliberate design goal&lt;/li&gt;
&lt;li&gt;DI graphs are reviewed and curated as first-class artifacts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In other words, wire’s strengths are organizational and process-driven.&lt;/p&gt;

&lt;p&gt;If your project doesn’t need that level of explicitness, wire becomes harder to justify.&lt;/p&gt;




&lt;h2&gt;
  
  
  Practical takeaway
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Runtime DI (fx/dig, do)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Powerful, but often overkill for standard web APIs&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Static DI (wire)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sufficient for most APIs, but verbose&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Static + minimal (injector)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Same guarantees, less ceremony&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;For the common case—stateless web APIs with stable dependency graphs—&lt;strong&gt;static wiring is enough&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And if you can keep that wiring static &lt;em&gt;and&lt;/em&gt; reduce the amount of DI-specific code you maintain, that’s an even better outcome.&lt;/p&gt;




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

&lt;p&gt;Dependency Injection doesn’t need to be a framework-level decision for most Go web APIs. It’s a startup detail.&lt;/p&gt;

&lt;p&gt;Resolve dependencies once. Generate plain Go code. Keep the wiring boring.&lt;/p&gt;

&lt;p&gt;If wire already feels sufficient—but a bit heavy—the field-tag-based approach is a natural next step.&lt;/p&gt;

&lt;p&gt;In that sense, &lt;strong&gt;injector is not a radical alternative&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It’s simply asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;If static DI is enough… why not make it simpler?&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>go</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>Dependency Injection in Go, Reduced to Field Tags</title>
      <dc:creator>Tetsuro Mikami</dc:creator>
      <pubDate>Thu, 18 Dec 2025 01:47:08 +0000</pubDate>
      <link>https://dev.to/mickamy/dependency-injection-in-go-reduced-to-field-tags-4h1o</link>
      <guid>https://dev.to/mickamy/dependency-injection-in-go-reduced-to-field-tags-4h1o</guid>
      <description>&lt;p&gt;Dependency Injection (DI) in Go often feels heavier than it should be.&lt;/p&gt;

&lt;p&gt;Once a project grows, you start seeing patterns like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Large provider sets&lt;/li&gt;
&lt;li&gt;Dedicated wiring files (for tools like &lt;code&gt;wire&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Initialization order that humans must carefully maintain&lt;/li&gt;
&lt;li&gt;DI-specific code spreading across the codebase&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Over time, the amount of code written &lt;em&gt;for DI itself&lt;/em&gt; can rival the actual business logic.&lt;/p&gt;

&lt;p&gt;I wanted to see how far DI could be simplified in Go.&lt;/p&gt;

&lt;p&gt;That experiment became &lt;strong&gt;injector&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Core Idea
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;All dependency injection is declared using struct field tags.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Nothing else.&lt;/p&gt;

&lt;p&gt;No provider sets.&lt;br&gt;
No DSL.&lt;br&gt;
No runtime reflection.&lt;/p&gt;

&lt;p&gt;The container declares &lt;em&gt;what&lt;/em&gt; is injected.&lt;br&gt;
Providers declare &lt;em&gt;how&lt;/em&gt; values are constructed.&lt;/p&gt;


&lt;h2&gt;
  
  
  The Container
&lt;/h2&gt;

&lt;p&gt;A container is just a struct. Fields marked with the &lt;code&gt;inject&lt;/code&gt; tag are managed by injector.&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;type&lt;/span&gt; &lt;span class="n"&gt;Container&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;UserService&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UserService&lt;/span&gt; &lt;span class="s"&gt;`inject:""`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;inject&lt;/code&gt; tag is intentionally minimal:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It is a marker only&lt;/li&gt;
&lt;li&gt;It carries no configuration by default&lt;/li&gt;
&lt;li&gt;It simply means “this field is injected by injector”&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Providers
&lt;/h2&gt;

&lt;p&gt;A provider is any top-level function that returns a value.&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;NewUserService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="n"&gt;infra&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Database&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UserService&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;userService&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;DB&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;db&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;Rules are simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The function must be top-level (no receiver)&lt;/li&gt;
&lt;li&gt;Parameters are dependencies&lt;/li&gt;
&lt;li&gt;The return value is the provided type&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Injector discovers providers automatically via static analysis.&lt;/p&gt;




&lt;h2&gt;
  
  
  Generated Code
&lt;/h2&gt;

&lt;p&gt;Running the generator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;injector generate ./...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Produces plain Go code:&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;NewContainer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Container&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;NewDatabaseConfig&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&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;cfg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;NewUserService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&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;Container&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;UserService&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;user&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;There is no runtime magic.&lt;br&gt;
The generated code is readable, debuggable, and type-safe.&lt;/p&gt;


&lt;h2&gt;
  
  
  Interface-First by Default
&lt;/h2&gt;

&lt;p&gt;Injector works naturally with interfaces.&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;type&lt;/span&gt; &lt;span class="n"&gt;UserService&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="kt"&gt;string&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;func&lt;/span&gt; &lt;span class="n"&gt;NewUserService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="n"&gt;infra&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Database&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;UserService&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;userService&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;DB&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;db&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;The container exposes only interfaces.&lt;br&gt;
Concrete implementations remain private.&lt;/p&gt;

&lt;p&gt;This keeps application boundaries clean without adding DI-specific abstractions.&lt;/p&gt;


&lt;h2&gt;
  
  
  Handling Multiple Providers
&lt;/h2&gt;

&lt;p&gt;If multiple providers return the same type, injector requires an explicit choice.&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;type&lt;/span&gt; &lt;span class="n"&gt;Container&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;_&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DatabaseConfig&lt;/span&gt; &lt;span class="s"&gt;`inject:"provider:NewPrimaryDatabaseConfig"`&lt;/span&gt;
    &lt;span class="n"&gt;UserService&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UserService&lt;/span&gt; &lt;span class="s"&gt;`inject`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A blank (&lt;code&gt;_&lt;/code&gt;) field:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Does not expose the dependency&lt;/li&gt;
&lt;li&gt;Declares a provider override&lt;/li&gt;
&lt;li&gt;Applies globally within the container&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Provider selection remains centralized and explicit.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Field Tags?
&lt;/h2&gt;

&lt;p&gt;Go structs already represent dependency lists.&lt;/p&gt;

&lt;p&gt;Adding extra configuration layers often makes DI harder to reason about, not easier.&lt;/p&gt;

&lt;p&gt;By limiting DI declarations to field tags:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dependencies are visible at a glance&lt;/li&gt;
&lt;li&gt;Configuration stays local&lt;/li&gt;
&lt;li&gt;The mental model stays small&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;DI should be infrastructure, not the focus of the codebase.&lt;/p&gt;




&lt;h2&gt;
  
  
  Status
&lt;/h2&gt;

&lt;p&gt;Injector is intentionally small and opinionated.&lt;/p&gt;

&lt;p&gt;The current goal is not feature breadth, but clarity:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Marker-based containers&lt;/li&gt;
&lt;li&gt;Provider-based resolution&lt;/li&gt;
&lt;li&gt;Compile-time dependency graphs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Future ideas exist, but they can wait until real-world usage drives them.&lt;/p&gt;




&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/mickamy/injector" rel="noopener noreferrer"&gt;https://github.com/mickamy/injector&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Feedback is very welcome — especially around the design trade-offs and edge cases.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>go</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
