見出し、例、コード、表、リンク、参照画像を含む原文を表示しています。
Persona: You are a Go architect using wire for compile-time DI. You let the compiler find missing dependencies, treat wire_gen.go as committed source, and re-run wire ./... after every graph change.
Dependencies:
- wire:
go install github.com/google/wire/cmd/wire@latest
When to use: any task centered on google/wire. Runtime containers with lifecycle and modularity are golang-uber-dig / golang-uber-fx; the concept-level comparison lives in golang-dependency-injection. Note that google/wire was archived in August 2025 — feature-complete, with bug fixes still accepted.
How it differs from runtime DI
| wire | dig / fx / samber/do | |
|---|---|---|
| Resolution | compile-time codegen | runtime reflection/generics |
| Failure timing | wire ./... errors now | first Invoke / startup |
| Runtime container | none — plain Go calls | present |
| Lifecycle hooks | none built in | fx: OnStart/OnStop |
| Generated file | wire_gen.go, committed | none |
The payoff: the whole graph either builds or the tool tells you exactly which dependency is missing.
Providers and sets
A provider is any function whose parameters are dependencies and whose results are provided types. It may return a value, (value, error), or (value, cleanup, error):
func NewConfig() *Config { return &Config{Addr: ":8080"} }
func NewDB(cfg *Config) (*sql.DB, error) { return sql.Open("postgres", cfg.DSN) }
func NewRedis(cfg *Config) (*redis.Client, func(), error) {
c := redis.NewClient(&redis.Options{Addr: cfg.RedisAddr})
return c, func() { c.Close() }, nil
}wire.NewSet bundles providers for reuse and may reference other sets:
var InfraSet = wire.NewSet(NewConfig, NewDB, NewRedis)
var ServiceSet = wire.NewSet(NewUserRepo, NewUserService,
wire.Bind(new(UserStore), new(*UserRepo)),
)Keep sets stable — library sets are a contract; adding inputs or removing outputs breaks downstream injectors. One set per package is a sane default.
Injector files and //go:build wireinject
The injector file declares the function wire will generate the body for:
//go:build wireinject
package main
import "github.com/google/wire"
func InitApp() (*App, func(), error) {
wire.Build(InfraSet, ServiceSet, NewApp)
return nil, nil, nil // replaced by codegen
}The build tag keeps the stub out of the binary — only the untagged wire_gen.go compiles. Drop the tag and both files define the same function (a duplicate-symbol error). If the dummy returns annoy you, panic(wire.Build(...)) works too.
Run wire ./... after every constructor-signature change; wire check ./... validates without regenerating (cheap for CI). Add //go:generate go run github.com/google/wire/cmd/wire so go generate ./... covers it, and commit wire_gen.go.
Interface bindings
Wire refuses to infer interface satisfaction — you must declare it, so a new implementor elsewhere can't silently make the graph ambiguous:
wire.Bind(new(UserStore), new(*PostgresUserRepo))Structs, values, fields
wire.Struct(new(Server), "Logger", "DB")fills named fields from the graph ("*"fills all non-wire:"-"fields).wire.Value(Foo{X: 42})supplies a constant (no calls or channels).wire.InterfaceValue(new(io.Reader), os.Stdin)supplies an interface-typed literal.wire.FieldsOf(new(Config), "DSN")promotes a field as a graph node.
See advanced for the wire:"-" tag and field promotion details.
Duplicate types
One provider per type, strictly. Disambiguate with named types rather than two functions returning *sql.DB:
type PrimaryDSN string
type ReplicaDSN stringCleanup chains
Cleanup providers return (T, func(), error); wire chains them in reverse order and, if construction fails midway, runs only the cleanups already built. In main, guard the cleanup against nil:
app, cleanup, err := InitApp()
if err != nil {
log.Fatal(err)
}
if cleanup != nil {
defer cleanup()
}Full shape
//go:build wireinject
package main
func InitApp() (*App, func(), error) {
wire.Build(config.ConfigSet, infra.InfraSet, service.ServiceSet, NewApp)
return nil, nil, nil
}
// main.go
func main() {
app, cleanup, err := InitApp()
if err != nil {
log.Fatal(err)
}
defer cleanup()
app.Run()
}A complete decorated example with per-package sets and cleanup-heavy graphs: recipes.
Best practices
- Never hand-edit
wire_gen.go; it is a committed build artifact. - Build-tag every injector file, first line, or suffers duplicate symbols.
- Named types over raw types for same-underlying duplicates.
- Keep library sets additive and backward-compatible.
- Return
(T, func(), error)from cleanup providers and let wire order it. - One injector per file; delegate to per-package sets instead of fat
wire.Buildlists.
Common mistakes
| Mistake | Fix |
|---|---|
Editing wire_gen.go | change providers, re-run wire ./... |
Missing //go:build wireinject | tag the first line of injector files |
Two *sql.DB providers | named struct types, one provider each |
Interface without wire.Bind | add the bind to the set |
| Forgetting re-run after changes | wire before go build; wire a Makefile target |
Unguarded cleanup() | nil-safe: if cleanup != nil { defer cleanup() } |
Testing
wire emits plain constructors, so tests inject by hand — nothing to clone or reset. Test injectors that swap real providers for fakes, plus a CI stale check for wire_gen.go: testing.
Cross-references
golang-dependency-injection— when DI pays off and the full library matrix.golang-uber-dig/golang-uber-fx— runtime reflection-based containers.golang-samber-do— generics-based container, no codegen.golang-structs-interfaces— interface design for constructor inputs/outputs.golang-testing— general testing patterns.
Library bugs: google/wire issues.

