Generated banner for Golang Uber Dig
Generated logo for Golang Uber Dig

Golang Uber Dig

Samuel BertheCommunityCreated by samberDistributed by Samuel Berthe

  • Skill
  • Tool integration
  • Software development
  • MIT
  • No issues found

From the author

Implements dependency injection in Golang using uber-go/dig — reflection-based container, Provide/Invoke, dig.In/dig.Out parameter and result objects, named values, value groups, optional dependencies, scopes, and Decorate. Apply when using or adopting uber-go/dig, when the codebase imports `go.uber.org/dig`, or when wiring an application graph at startup. For higher-level lifecycle and modules, see `samber/cc-skills-golang@golang-uber-fx` skill.
— the description by samber

AutoGineer summary

Written by AutoGineer from the skill’s own files, not by the author

The golang-uber-dig skill provides guidance and patterns for using the uber-go/dig library to manage dependency injection in Go. It covers the core container mechanics, including constructor registration, dependency resolution, parameter and result objects, named values, and value groups. This skill is designed for developers who need to wire application graphs at startup and prefer a reflection-based approach to managing object lifecycles.

What it does

  • Registers constructors for lazy dependency injection using Provide.
  • Resolves object graphs and executes logic using Invoke.
  • Groups dependencies using dig.In and dig.Out structs.
  • Handles complex wiring scenarios with named values and value groups.
  • Exposes concrete types as interfaces using dig.As.
  • Supports advanced features like decorators and child scopes for modularity.

When to use it

  • When you need to wire an application graph at startup.
  • When building CLI tools or libraries that require a dependency container.
  • When you need to manage complex dependency relationships in a Go application.
  • When you want to decouple component creation from application logic.

Advantages

  • Reduces manual wiring boilerplate in complex applications.
  • Enforces clean architecture by encouraging dependency on interfaces.
  • Provides clear error reporting with dependency paths upon failure.
  • Supports modular design through scopes and decorators.

Usage examples

“How do I register a database connection in my dig container?”
You use c.Provide(func() (*sql.DB, error) { ... }) to register the constructor, which dig will call lazily when a component requires the *sql.DB type.
“How can I group multiple HTTP handlers into a single slice for my server?”
You use the dig.Group tag in a result struct (dig.Out) to tag each handler, then consume them in a parameter struct (dig.In) as a slice using the same group tag.

Install

  1. Download the skill as a ZIP:

    https://autogineer.com/agent-skills/samber/golang-uber-dig/download
  2. Unzip it into the cross-client skills folder of your project:

    mkdir -p .agents/skills && unzip golang-uber-dig.zip -d .agents/skills

Documentation by samber

Persona: You are a Go architect wiring an application graph with dig. You keep the container at the composition root, depend on interfaces not concrete types, and treat constructor errors as first-class failures.

Using uber-go/dig for Dependency Injection in Go

Reflection-based DI toolkit, designed to power application frameworks (it is the engine behind uber-go/fx) and resolve object graphs during startup.

Official Resources:

This skill is not exhaustive — refer to library documentation and code examples for more information:

  • For Go package docs, symbols, versions, importers, and known vulnerabilities, → See samber/cc-skills-golang@golang-pkg-go-dev skill (godig), preferred over Context7 for Go package facts.

  • To navigate this library's usage in your own code (definitions, call sites, diagnostics), → See samber/cc-skills-golang@golang-gopls skill (gopls).

  • Context7 remains a fallback for docs not indexed on pkg.go.dev.

go get go.uber.org/dig

dig vs. fx

fx is built on dig and shares the same container engine — the DI primitives (Provide, Invoke, In/Out structs, named values, value groups) are identical. fx.In/fx.Out are re-exports of dig.In/dig.Out.

What fx adds on top of dig:

Concerndigfx
DI container✅ dig.New()✅ (embedded)
Lifecycle hooks❌✅ fx.Lifecycle OnStart/OnStop
Module system❌✅ fx.Module with scoped decorators
Signal-aware run loop❌✅ app.Run() blocks on SIGINT/SIGTERM
Structured event logging❌✅ fx.WithLogger / fxevent
Startup/shutdown timeout❌✅ fx.StartTimeout / fx.StopTimeout

Choose dig when you need the wiring graph only: CLI tools, libraries exposing a container to callers, test harnesses, or embedding DI into an existing app that manages its own lifecycle.

Choose fx for long-running services (HTTP servers, workers, daemons) — lifecycle and signal handling are non-negotiable there. See samber/cc-skills-golang@golang-uber-fx skill.

Container

import "go.uber.org/dig"

c := dig.New()

Useful options: dig.DeferAcyclicVerification() (faster startup), dig.RecoverFromPanics() (turn panics into dig.PanicError), dig.DryRun(true) (validate without invoking).

Provide and Invoke

// Register a constructor — lazy, only runs when its output is needed
err := c.Provide(func(cfg *Config) (*sql.DB, error) {
    return sql.Open("postgres", cfg.DSN)
})

// Pull a service out of the container by asking for it as a function parameter
err = c.Invoke(func(db *sql.DB) error {
    return db.Ping()
})

Constructors are lazy and memoized: each output type is built once and shared (singleton per container). Provide errors at registration if the constructor is malformed; Invoke returns the constructor's error wrapped with the dependency path that triggered it.

A dig constructor is any function whose inputs are dependencies and whose outputs are provided types. error (last return) signals construction failure. Follow "accept interfaces, return structs".

Parameter Objects with dig.In

Once a constructor has 4+ dependencies, embed dig.In to group them as struct fields and tag fields:

type HandlerParams struct {
    dig.In

    Logger *zap.Logger
    DB     *sql.DB
    Cache  *redis.Client `optional:"true"`           // zero value if not provided
    DBRO   *sql.DB       `name:"readonly"`           // named dependency
    Routes []http.Handler `group:"routes"`           // value group
}

func NewHandler(p HandlerParams) *Handler { /* ... */ }

Tags: name:"...", optional:"true", group:"...".

Result Objects with dig.Out

Return several values from one constructor and attach name/group tags to results:

type ConnResult struct {
    dig.Out

    ReadWrite *sql.DB `name:"primary"`
    ReadOnly  *sql.DB `name:"readonly"`
}

func NewConnections(cfg *Config) (ConnResult, error) { /* ... */ }

Named Values

Two providers of the same type collide. Disambiguate with dig.Name:

c.Provide(NewPrimaryDB,  dig.Name("primary"))
c.Provide(NewReadOnlyDB, dig.Name("readonly"))

Consume by adding name:"primary" / name:"readonly" to a dig.In field.

Value Groups

Many providers, one consumer slice — typical for HTTP handlers, health checks, migrations:

type RouteResult struct {
    dig.Out
    Handler http.Handler `group:"routes"`
}

func NewUserHandler(db *sql.DB) RouteResult { /* ... */ }
func NewPostHandler(db *sql.DB) RouteResult { /* ... */ }

type ServerParams struct {
    dig.In
    Routes []http.Handler `group:"routes"`
}

Flatten — append ,flatten (e.g. group:"routes,flatten") to unwrap a slice instead of nesting it. Group order is not guaranteed; if order matters, provide an explicit ordered slice from a single constructor.

Provide as Interface (dig.As)

Register a concrete constructor and expose it under one or more interfaces without a separate adapter:

c.Provide(NewPostgresDB, dig.As(new(Database), new(io.Closer)))
// Consumers ask for Database or io.Closer; *PostgresDB stays hidden.

Full Application Example

func main() {
    c := dig.New()

    must(c.Provide(NewConfig))
    must(c.Provide(NewLogger))
    must(c.Provide(NewDatabase))
    must(c.Provide(NewServer))

    err := c.Invoke(func(srv *http.Server) error {
        return srv.ListenAndServe()
    })
    if err != nil {
        log.Fatal(err)
    }
}

func must(err error) { if err != nil { panic(err) } }

dig has no built-in lifecycle. If you need OnStart/OnStop hooks, signal handling, and graceful shutdown, use fx — see samber/cc-skills-golang@golang-uber-fx skill.

For Decorate, Scopes, optional deps, error helpers, and Visualize, see advanced.md.

Best Practices

  1. Keep the container at the composition root — never pass *dig.Container as a parameter; treat it like a plumbing detail of main(). Service-locator patterns defeat the testability gains of DI.

  2. Depend on interfaces, not concrete types — lets you swap implementations in tests without touching production code, and lets you use dig.As to expose narrow interfaces from wide structs.

  3. Prefer parameter objects (dig.In structs) once a constructor has 4+ dependencies — call sites stay readable and adding a new dependency is a one-line change instead of a signature break.

  4. Group registration by module (one file per module that calls c.Provide for its types) — review and refactoring become a per-module concern, and you can extract a module into a fx.Module later without rewriting wiring.

  5. Validate the graph eagerly in tests — call c.Invoke against the composition root in CI to surface missing providers at boot time, not at first request. DryRun(true) skips constructor execution.

  6. Return errors from constructors instead of panicking — dig wraps them with the dependency path, which makes the failure point obvious.

Common Mistakes

MistakeFix
Passing the container into servicesThe container belongs to main(). Inject the typed dependencies a service needs; otherwise tests need to build a real container.
Two providers for the same type without Namedig errors at Provide time. Either name them, or merge into a single provider that returns a dig.Out result struct.
Ignoring Provide errorsWrap each Provide with a must helper. A silent registration error becomes a missing-type error far later.
Using groups when ordering mattersGroups are unordered. If order matters (middleware chain, migration sequence), provide an explicit ordered slice with one constructor.
Constructors with side effects on importKeep init() empty — start work only inside the constructor, after the graph is built.

Testing

dig containers are cheap — build a fresh one per test, override providers with Decorate, and call Invoke to drive the system. For full patterns (per-test wiring, shared helpers, graph validation in CI, asserting wire-time errors, recovering from constructor panics), see testing.md.

Further Reading

  • advanced.md — Decorate, Scopes, optional deps, error helpers, Visualize, full Quick Reference

  • recipes.md — end-to-end examples: HTTP server with route group, two databases, request scopes, decorators, dry-run validation

  • testing.md — testing patterns and graph validation

Cross-References

  • → See samber/cc-skills-golang@golang-uber-fx skill for application lifecycle, modules, and signal-aware Run() built on top of dig

  • → See samber/cc-skills-golang@golang-dependency-injection skill for DI concepts and library comparison

  • → See samber/cc-skills-golang@golang-samber-do skill for a generics-based alternative without reflection

  • → See samber/cc-skills-golang@golang-google-wire skill for compile-time DI (no runtime container)

  • → See samber/cc-skills-golang@golang-structs-interfaces skill for interface design patterns

  • → See samber/cc-skills-golang@golang-testing skill for general testing patterns

If you encounter a bug or unexpected behavior in uber-go/dig, open an issue at https://github.com/uber-go/dig/issues.

Files

evals/evals.json

[
  {
    "id": 1,
    "name": "param-objects-many-deps",
    "description": "Tests use of dig.In parameter objects when a constructor has many dependencies",
    "prompt": "I'm wiring a Go service with uber-go/dig. I have a NewServer constructor that needs *zap.Logger, *sql.DB, *redis.Client, *Config, and *MetricsRegistry. The signature is getting unwieldy. How should I clean it up?",
    "trap": "Without the skill, the model keeps the long signature or wraps args in an ad-hoc struct without dig.In, missing the parameter-object pattern that lets the container fill the fields.",
    "assertions": [
      {"id": "1.1", "text": "Embeds dig.In in the parameter struct"},
      {"id": "1.2", "text": "Constructor takes the params struct as a single argument"},
      {"id": "1.3", "text": "Does NOT just keep the long parameter list as the answer"},
      {"id": "1.4", "text": "Does NOT use a plain struct without dig.In (which would not be filled by the container)"},
      {"id": "1.5", "text": "Mentions readability/maintainability benefit (adding a new dep is a one-line change)"}
    ]
  },
  {
    "id": 2,
    "name": "value-groups-for-handlers",
    "description": "Tests value groups when many constructors must contribute to one slice",
    "prompt": "In my Go HTTP server wired with uber-go/dig, I have several constructors (NewUserHandler, NewPostHandler, NewHealthHandler) and a NewRouter that should consume all of them. Each handler is registered independently. How do I wire this without the router knowing which handlers exist?",
    "trap": "Without the skill, the model invokes each handler individually inside main() and passes a slice to NewRouter, missing the group:\"...\" tag that decouples producers from consumers.",
    "assertions": [
      {"id": "2.1", "text": "Each handler constructor returns a dig.Out struct (or uses dig.Group via Provide option) tagged with group:\"routes\" (or similar group name)"},
      {"id": "2.2", "text": "NewRouter consumes a dig.In with a slice field tagged group:\"routes\""},
      {"id": "2.3", "text": "Handlers are added with c.Provide — no manual slice assembly in main()"},
      {"id": "2.4", "text": "Does NOT manually assemble the handler slice and pass it to NewRouter"},
      {"id": "2.5", "text": "Mentions that group order is not guaranteed (or is silent on it; does NOT claim a specific order is guaranteed)"}
    ]
  },
  {
    "id": 3,
    "name": "named-values-multiple-dbs",
    "description": "Tests dig.Name for multiple instances of the same type",
    "prompt": "My Go app needs two *sql.DB connections — one to the primary write database and one to a read replica. Both use the same *sql.DB type. Show me how to register and consume them with uber-go/dig.",
    "trap": "Without the skill, the model wraps the two connections in different types (struct PrimaryDB / struct ReadOnlyDB) instead of using dig.Name on a single type.",
    "assertions": [
      {"id": "3.1", "text": "Uses dig.Name(\"...\") on c.Provide for at least one of the two databases (or uses dig.Out result tags name:\"...\")"},
      {"id": "3.2", "text": "Both providers register the same *sql.DB type, distinguished by name"},
      {"id": "3.3", "text": "Consumer uses dig.In with name:\"primary\" / name:\"readonly\" tags"},
      {"id": "3.4", "text": "Does NOT introduce wrapper types like type PrimaryDB *sql.DB just to disambiguate"},
      {"id": "3.5", "text": "Does NOT register both as plain *sql.DB without names (which dig rejects at Provide time)"}
    ]
  },
  {
    "id": 4,
    "name": "as-to-hide-concrete",
    "description": "Tests dig.As to expose only an interface to consumers",
    "prompt": "I have a *PostgresDB concrete struct in my Go code that has many internal fields and methods. I want consumers to depend only on a Database interface (Query, Exec). How do I register it with uber-go/dig so consumers can never accidentally see the concrete type?",
    "trap": "Without the skill, the model registers the constructor returning Database (interface) directly, which works but loses type info; or writes a separate adapter constructor — missing dig.As that does this in one line.",
    "assertions": [
      {"id": "4.1", "text": "Uses dig.As(new(Database)) as a Provide option on the *PostgresDB constructor"},
      {"id": "4.2", "text": "The constructor itself returns the concrete *PostgresDB"},
      {"id": "4.3", "text": "Consumers ask for the Database interface, not *PostgresDB"},
      {"id": "4.4", "text": "Does NOT write a separate wrapper/adapter constructor that just returns the interface"},
      {"id": "4.5", "text": "Mentions or demonstrates that only the interface is exposed in the graph"}
    ]
  },
  {
    "id": 5,
    "name": "scopes-for-request-locals",
    "description": "Tests scopes for per-request dependencies",
    "prompt": "In my Go web app wired with uber-go/dig, I have global services (logger, database) and per-request data (current user, request ID). How do I keep request-scoped values from leaking across concurrent requests?",
    "trap": "Without the skill, the model registers everything in the root container and uses context.Value for per-request data — missing dig.Scope which provides isolated child containers.",
    "assertions": [
      {"id": "5.1", "text": "Uses c.Scope(\"request\") (or root.Scope) to create a child container per request"},
      {"id": "5.2", "text": "Global services (logger, database) stay registered on the root"},
      {"id": "5.3", "text": "Per-request providers are registered on the request scope, not on the root"},
      {"id": "5.4", "text": "Mentions that the child scope inherits root providers"},
      {"id": "5.5", "text": "Does NOT register per-request providers globally where they would be shared across requests"}
    ]
  },
  {
    "id": 6,
    "name": "container-not-passed-around",
    "description": "Tests that the container stays at the composition root",
    "prompt": "I'm using uber-go/dig in my Go service. My UserHandler depends on UserService and AuditLogger. Should I inject *dig.Container into UserHandler so it can resolve its own dependencies on demand?",
    "trap": "Without the skill, the model agrees to pass the container, turning it into a service-locator anti-pattern that hides dependencies and breaks testability.",
    "assertions": [
      {"id": "6.1", "text": "Advises against passing *dig.Container into business code"},
      {"id": "6.2", "text": "States that the container should only live at the composition root (main / startup)"},
      {"id": "6.3", "text": "Recommends UserHandler take typed dependencies (UserService, AuditLogger) as constructor parameters"},
      {"id": "6.4", "text": "Mentions service locator anti-pattern OR explains the testability/visibility downside"},
      {"id": "6.5", "text": "Does NOT show example code that injects the container into a handler"}
    ]
  },
  {
    "id": 7,
    "name": "graph-validation-dryrun",
    "description": "Tests dig.DryRun for validating the graph in CI without running constructors",
    "prompt": "I want a Go test that catches missing-provider errors and cycles in my uber-go/dig wiring without actually starting database connections, HTTP servers, or any real side effects. How do I write that test?",
    "trap": "Without the skill, the model spins up a real container with real constructors, or skips validation entirely — missing dig.DryRun(true) which validates types without invocation.",
    "assertions": [
      {"id": "7.1", "text": "Uses dig.New(dig.DryRun(true)) to build the test container"},
      {"id": "7.2", "text": "Registers the same Provides as the production binary"},
      {"id": "7.3", "text": "Calls c.Invoke against the composition root (or top-level dependency) to trigger validation"},
      {"id": "7.4", "text": "Asserts no error is returned"},
      {"id": "7.5", "text": "Does NOT actually instantiate real services in the test"}
    ]
  },
  {
    "id": 8,
    "name": "decorate-not-rewrite",
    "description": "Tests Decorate to wrap an existing value (e.g., logger) instead of replacing the constructor",
    "prompt": "In my Go app using uber-go/dig, I want every component in the 'worker' module to receive a *zap.Logger that is named 'worker' (i.e., adds a 'logger':'worker' field). Other modules should keep the unnamed logger. What's the cleanest way?",
    "trap": "Without the skill, the model rewrites NewLogger or wraps every constructor manually — missing c.Decorate which transforms the value at the scope/module boundary.",
    "assertions": [
      {"id": "8.1", "text": "Uses Decorate (c.Decorate or scope.Decorate) on *zap.Logger"},
      {"id": "8.2", "text": "The decorator returns log.Named(\"worker\") (or equivalent)"},
      {"id": "8.3", "text": "Decorate is applied at the worker scope/module, not globally"},
      {"id": "8.4", "text": "Does NOT modify or duplicate the original NewLogger constructor"},
      {"id": "8.5", "text": "Mentions decorator scope semantics (applies to scope and descendants only) OR places Decorate inside a scope"}
    ]
  },
  {
    "id": 9,
    "name": "panic-recovery-option",
    "description": "Tests RecoverFromPanics container option",
    "prompt": "Some third-party constructors I'm registering with uber-go/dig occasionally panic on invalid configuration. I want my Go app to convert those panics into error returns from c.Invoke instead of crashing the process. How do I configure the container?",
    "trap": "Without the skill, the model wraps every constructor in defer/recover — missing dig.RecoverFromPanics() which does this at the container level.",
    "assertions": [
      {"id": "9.1", "text": "Uses dig.New(dig.RecoverFromPanics()) (or passes the option to dig.New)"},
      {"id": "9.2", "text": "Mentions or shows that the panic surfaces as a typed dig.PanicError"},
      {"id": "9.3", "text": "Does NOT recommend wrapping every constructor in defer recover() manually"},
      {"id": "9.4", "text": "Uses errors.As(err, &dig.PanicError{}) or equivalent to detect the panic case"}
    ]
  },
  {
    "id": 10,
    "name": "groups-flatten-tag",
    "description": "Tests group:\",flatten\" tag when one constructor produces multiple group entries",
    "prompt": "I have a NewMigrations constructor in my Go app (using uber-go/dig) that returns a slice of Migration values. I want every Migration in this slice to be visible to consumers that consume the 'migrations' group as []Migration. How do I do this without nesting?",
    "trap": "Without the skill, the model registers []Migration as a single group entry, leaving consumers with [][]Migration. The right answer is the ',flatten' tag on the group.",
    "assertions": [
      {"id": "10.1", "text": "Uses group:\"migrations,flatten\" tag (with the flatten suffix)"},
      {"id": "10.2", "text": "Result struct has a slice field []Migration with the tag"},
      {"id": "10.3", "text": "Consumer receives []Migration (flat slice), not [][]Migration"},
      {"id": "10.4", "text": "Does NOT silently produce a nested-slice consumer signature"}
    ]
  },
  {
    "id": 11,
    "name": "fx-vs-dig-when-lifecycle",
    "description": "Tests recommending fx (instead of raw dig) when the user needs lifecycle/signal handling",
    "prompt": "I'm starting a new Go service with uber-go/dig. The service needs to gracefully shut down on SIGTERM, run startup migrations, and start a background worker. Should I implement signal handling and start/stop sequencing on top of dig myself?",
    "trap": "Without the skill, the model writes custom signal handling code on top of raw dig — missing that uber-go/fx is built specifically for this and provides fx.Lifecycle, fx.Hook, and signal-aware Run().",
    "assertions": [
      {"id": "11.1", "text": "Recommends migrating to or considering uber-go/fx for the lifecycle requirements"},
      {"id": "11.2", "text": "Mentions that fx is built on top of dig (so existing wiring patterns transfer)"},
      {"id": "11.3", "text": "Mentions fx.Lifecycle / fx.Hook (OnStart/OnStop) for graceful boot/shutdown"},
      {"id": "11.4", "text": "Mentions app.Run() handling SIGINT/SIGTERM"},
      {"id": "11.5", "text": "Does NOT walk the user through writing custom signal handling on top of raw dig as the primary recommendation"}
    ]
  }
]

Requirements and compatibility

Written for: Claude Code, OpenAI Codex

Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta

“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.

Needs: Declares allowed tools, Claude Code frontmatter

The author says: Designed for Claude Code, Codex or similar harness, and for projects using Golang.

Security

No issues found by our static scan· No issues found

A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.

Attribution and licence

Created by
samber
Publisher
Samuel Berthe (Community)
Distributed by
Samuel Berthe
Source
samber/cc-skills-golang · skills/golang-uber-dig · commit bb1d43d
Licence
MIT License (redistribution allowed)
  • LICENSE (added by AutoGineer from LICENSE)
  • ATTRIBUTION.md (written by AutoGineer)
Synced

AutoGineer is not affiliated with or endorsed by samber or Samuel Berthe.

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download golang-uber-dig.zip