Skip to content
Back to skills

Golang Dependency Injection

ASecurity

This skill should be used when the user asks to "wire up dependencies in Go without a framework", "should I use wire, dig, fx, or samber/do", "generate a Go dependency injection container", "add a lifecycle hook to an fx app", "why does my dig container fail to invoke", or is choosing between or implementing google/wire, uber-go/dig, uber-go/fx, or samber/do for constructing a Go application's dependency graph. go-service-idioms's default is manual constructor functions wired by hand in main(...

  • 2 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added October 16, 2026
developmentgosqlgit

Security analysis

A100/100

Pro scans all 2 files and shows the line behind each finding

Scanned October 16, 2026

npx -y skills add shennawardana23/skillme --skill golang-dependency-injection --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Golang Dependency Injection?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Golang Dependency Injection
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/shennawardana23-golang-dependency-injection/badge)](https://www.skillsdirectory.com/skills/shennawardana23-golang-dependency-injection)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: golang-dependency-injection
description: This skill should be used when the user asks to "wire up dependencies in Go without a framework", "should I use wire, dig, fx, or samber/do", "generate a Go dependency injection container", "add a lifecycle hook to an fx app", "why does my dig container fail to invoke", or is choosing between or implementing google/wire, uber-go/dig, uber-go/fx, or samber/do for constructing a Go application's dependency graph. go-service-idioms's default is manual constructor functions wired by hand in main() — reach for this skill once that manual wiring gets unwieldy enough to justify a tool, and to pick the right one.
license: Apache-2.0
metadata:
  version: "0.1.0"
  category: "go"
---

# Go Dependency Injection

Manual constructor wiring in `main()` — call each constructor in
dependency order, pass the results down — is the correct default for
most Go services; it's explicit, has zero magic, and any Go developer can
read it without knowing a DI library. Reach for a tool only once that
wiring genuinely becomes hard to follow (dozens of components, several
optional/environment-specific variants) — and then pick based on the
table below, not on familiarity alone.

## Picking one

| Tool | Mechanism | Errors surface | Reach for it when |
|---|---|---|---|
| `google/wire` | Compile-time code generation — `wire.Build` inside a `//go:build wireinject` injector function produces a real `wire_gen.go` | Build time (missing/ambiguous provider is a codegen failure, not a runtime panic) | You want DI with zero runtime reflection cost and errors caught before the binary even runs |
| `uber-go/dig` | Runtime reflection — a `*dig.Container` resolves constructor arguments by type | Runtime, at `Invoke`/`Provide` call time | You want a lightweight container without wire's code-generation step, and runtime resolution cost is acceptable |
| `uber-go/fx` | Built on `dig`, adds application lifecycle (`OnStart`/`OnStop` hooks) and module composition | Runtime, at `fx.New`/app start | You're building a long-running service that needs coordinated startup/shutdown order across many components, not just object construction |
| `samber/do` (v2) | Runtime, generics-based (`Provide[T]`, `Invoke[T]`) instead of reflection over `interface{}` | Runtime, at `Invoke`/`MustInvoke` call time | You want dig/fx-like runtime DI but with compile-time type safety on the calling side via generics, and optionally its health-check/shutdown hooks |

Wire and dig/fx/do split along one axis: **compile-time (wire) vs.
runtime (dig, fx, do)**. Dig, fx, and do split along a second axis: dig
is a bare container, fx adds lifecycle management on top of dig, and
`samber/do` trades dig's `interface{}`-based reflection for Go generics
so `Invoke[MyService](injector)` is type-checked at compile time even
though resolution itself still happens at runtime.

## google/wire

```go
//go:build wireinject

func InitializeApp() (*App, error) {
    wire.Build(NewConfig, NewDB, NewStore, NewApp)
    return nil, nil // never actually runs; wire replaces this file
}
```

`wire.Build(providers...)` takes function values (each contributing its
first return type to the graph) and `wire.NewSet` groups reusable sets of
them. Running `wire` generates `wire_gen.go` with the real
`InitializeApp` implementation — the checked-in generated file, not the
injector template, is what actually ships. `wire.Bind(new(Iface),
new(*Impl))` maps an interface a constructor depends on to the concrete
type that satisfies it.

## uber-go/dig and uber-go/fx

```go
c := dig.New()
c.Provide(NewConfig)
c.Provide(NewDB)
c.Provide(NewStore)
if err := c.Invoke(func(s *Store) { s.Run() }); err != nil {
    log.Fatal(err)
}
```

`fx` wraps this in an `*fx.App` and adds `fx.Lifecycle`: a constructor
that needs to start/stop background work registers a `fx.Hook` via
`lifecycle.Append(fx.Hook{OnStart: ..., OnStop: ...})` instead of
managing goroutines by hand in `main()`:

```go
fx.New(
    fx.Provide(NewConfig, NewDB, NewServer),
    fx.Invoke(func(lc fx.Lifecycle, srv *Server) {
        lc.Append(fx.Hook{
            OnStart: func(ctx context.Context) error { return srv.Start(ctx) },
            OnStop:  func(ctx context.Context) error { return srv.Stop(ctx) },
        })
    }),
).Run()
```

`dig.In`/`dig.Out` structs let a constructor take or return several
values as named struct fields instead of a long parameter/return list;
`dig.Name`/`dig.Group` disambiguate or collect multiple values of the
same type.

## samber/do (v2)

```go
injector := do.New()
do.Provide(injector, func(i do.Injector) (*DB, error) { return NewDB(cfg) })
do.Provide(injector, func(i do.Injector) (*Store, error) {
    db := do.MustInvoke[*DB](i)
    return NewStore(db), nil
})
store := do.MustInvoke[*Store](injector)
```

`do.Provide[T]` registers a lazy constructor for `T`; `do.Invoke[T]`/
`do.MustInvoke[T]` resolve it, instantiating on first use. `do.Shutdown[T]`
and `do.HealthCheck[T]` give per-service lifecycle hooks similar in spirit
to fx's, but addressed by type via generics rather than fx's
lifecycle-object pattern.

## Gotchas

- Wire's injector function body is never actually executed — it exists
  only as a template `wire` reads to generate `wire_gen.go`. Editing the
  generated file by hand instead of re-running `wire` after changing the
  injector is a common mistake that silently drifts the two out of sync.
- A `dig`/`fx`/`do` missing-provider error only surfaces the first time
  that specific type is resolved — a container can build successfully
  and only fail deep into a request path the first time a rarely-used
  constructor is invoked, unlike wire's build-time failure for the same
  mistake.
- `fx.Invoke` functions run eagerly at app start in the order they're
  registered — a constructor with expensive or side-effecting logic
  should go in `fx.Provide` (lazy, resolved on demand) unless it
  genuinely needs to run unconditionally at startup.
- Value groups in `dig` (`dig.Group`) and named values both disambiguate
  "multiple things of the same type," but for different reasons —
  groups collect an unordered set (e.g., all registered HTTP
  middlewares), named values pick one specific instance among several
  (e.g., a "primary" vs. "replica" `*sql.DB`). Using named values where a
  group was meant loses the rest of the set silently.
- None of these four tools replace the "accept interfaces, return
  concrete structs" guidance in `go-service-idioms` — they automate
  *wiring* constructors together, not designing what those constructors'
  signatures should look like.

## Real-world grounding

`wire.Build`/`wire.NewSet`/`wire.Bind`, `dig.Container.Provide`/`Invoke`,
`fx.Lifecycle`/`fx.Hook`, and `do.Provide[T]`/`do.MustInvoke[T]` are
documented directly on pkg.go.dev under `github.com/google/wire`,
`go.uber.org/dig`, `go.uber.org/fx`, and `github.com/samber/do/v2`
respectively.

## Cross-references

- For the manual-wiring default and general package-layout guidance
  these tools automate around, see `go-service-idioms`.
- For lifecycle-hook goroutines (`OnStart`/`OnStop`) that themselves need
  correct cancellation, see `golang-concurrency` and `golang-context`.

Files in this skill

  • SKILL.md7.1 KB
  • evals/evals.json2.8 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.