Golang Google Wire
Samuel BertheCommunityCreated by samberDistributed by Samuel Berthe
- Skill
- Tool integration
- Software development
- MIT
- No issues found
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
google/wire is a code-generation toolkit for dependency injection in Go. Unlike runtime containers that use reflection, wire resolves the dependency graph at compile time and generates plain Go code. This approach ensures that missing dependencies or configuration errors are caught during the build process rather than at runtime. The workflow involves defining provider functions and injector stubs, then running the wire command to generate the final initialization code.
What it does
- Resolves complex dependency graphs at compile time
- Generates plain Go constructor code without reflection
- Supports interface binding and struct field injection
- Manages cleanup functions in reverse construction order
- Enforces strict type safety for dependency resolution
When to use it
- When you want to avoid runtime reflection for dependency injection
- When you need to catch dependency errors during compilation
- When building Go applications with complex object graphs
- When you prefer explicit, generated code over magic runtime containers
Advantages
- Zero runtime overhead
- Early error detection during build
- No magic or hidden runtime behavior
- Easy to debug as generated code is standard Go
- Supports complex cleanup chains automatically
Usage examples
- “Generate the dependency graph for my application.”
- The agent runs 'wire ./...' to process injector files and generate the 'wire_gen.go' file containing the necessary constructor calls.
- “How do I bind an interface to a concrete implementation in wire?”
- The agent provides a 'wire.Bind' example, showing how to map an interface type to a concrete struct provider within a 'wire.NewSet'.
- “My application has a cleanup function for the database connection.”
- The agent explains how to return a cleanup function from the provider and how wire automatically chains it in reverse order for safe shutdown.
Install
Download the skill as a ZIP:
https://autogineer.com/agent-skills/samber/golang-google-wire/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip golang-google-wire.zip -d .agents/skills
Documentation by samber
Persona: You are a Go architect using wire for compile-time DI. You let the compiler catch 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
Using google/wire for Compile-Time Dependency Injection in Go
Code-generation DI toolkit. Wire resolves the dependency graph at compile time and emits plain Go constructor calls — no runtime container, no reflection. Errors appear when you run wire ./..., not at first request.
Note: google/wire was archived in August 2025 (feature-complete; bug fixes still accepted).
Official Resources: pkg.go.dev · github.com/google/wire · User Guide · Best Practices
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-devskill (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-goplsskill (gopls).Context7 remains a fallback for docs not indexed on pkg.go.dev.
go get -tool github.com/google/wire/cmd/wire@latest
go get github.com/google/wirewire vs. Runtime DI
| Concern | wire | dig / fx / samber/do |
|---|---|---|
| Resolution | Compile time (codegen) | Runtime (reflection) |
| Error detection | wire ./... fails | First Invoke/startup |
| Runtime container | None — plain Go calls | Present |
| Lifecycle hooks | Not built in | fx: OnStart/OnStop |
| Generated files | wire_gen.go (committed) | None |
For lifecycle, lazy loading, and a full matrix see samber/cc-skills-golang@golang-dependency-injection.
Providers
A provider is any Go function — inputs are dependencies, outputs are provided types. Three return forms:
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) { // cleanup chained in reverse order
c := redis.NewClient(&redis.Options{Addr: cfg.RedisAddr})
return c, func() { c.Close() }, nil
}Provider Sets
wire.NewSet groups providers for reuse. Sets can reference other sets.
// infra/wire.go
var InfraSet = wire.NewSet(
NewConfig,
NewDB,
NewRedis,
)
// service/wire.go
var ServiceSet = wire.NewSet(
NewUserRepo,
NewUserService,
wire.Bind(new(UserStore), new(*UserRepo)), // interface binding
)Keep sets small: library sets expose a stable surface (adding inputs or removing outputs breaks downstream injectors). One set per package is a useful default.
Injectors and //go:build wireinject
The injector file declares the initialization function. Wire generates its body into wire_gen.go and replaces the stub.
//go:build wireinject
package main
import "github.com/google/wire"
// Wire generates the body of this function.
func InitApp() (*App, func(), error) {
wire.Build(InfraSet, ServiceSet, NewApp)
return nil, nil, nil // replaced by codegen
}The //go:build wireinject tag prevents the stub from being compiled into the binary — only wire_gen.go (which has no such tag) makes it through go build. Without this tag, both files define the same function, causing a compile error.
Alternative syntax when a dummy return is inconvenient:
func InitApp() (*App, func(), error) {
panic(wire.Build(InfraSet, ServiceSet, NewApp))
}Interface Bindings
Wire forbids implicit interface satisfaction — you must declare bindings explicitly so the graph is unambiguous when multiple types implement the same interface.
var Set = wire.NewSet(
NewPostgresUserRepo,
wire.Bind(new(UserStore), new(*PostgresUserRepo)), // tell wire: *PostgresUserRepo satisfies UserStore
)Explicit bindings prevent graph breakage when a new type implementing the same interface is added elsewhere.
Struct Providers and Values
wire.Struct fills struct fields from the graph without a manual constructor. Tag fields wire:"-" to exclude them.
wire.Struct(new(Server), "Logger", "DB") // inject named fields
wire.Struct(new(Server), "*") // inject all non-excluded fields
wire.Value(Foo{X: 42}) // constant expression (no fn calls / channels)
wire.InterfaceValue(new(io.Reader), os.Stdin) // interface-typed literal
wire.FieldsOf(new(Config), "DSN", "Addr") // promote struct fields as graph nodesSee advanced.md for the wire:"-" exclusion tag and wire.FieldsOf details.
Disambiguating Duplicate Types
Wire forbids two providers for the same type. Wrap the underlying type in distinct named types so each has exactly one provider:
type PrimaryDSN string
type ReplicaDSN stringFull Application Example
// wire.go — injector, excluded from binary via build tag
//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()
}Wire generates wire_gen.go (plain Go, committed, DO NOT EDIT). For a full example with per-package sets, cleanup-heavy graphs, and generated output, see recipes.md.
Codegen Workflow
wire ./... # regenerate all injectors in the module
wire check ./... # validate graph without regenerating (fast CI check)Run wire ./... after every constructor signature change. Add //go:generate go run github.com/google/wire/cmd/wire to injector files so go generate ./... also works. Commit wire_gen.go — it must stay in sync for CI builds.
Best Practices
Never edit
wire_gen.go— it is overwritten on everywire ./...run. Treat it as a build artifact that happens to be committed; source of truth is the provider and injector files.Always add
//go:build wireinjectto injector files — omitting it causes duplicate-symbol compile errors because both the stub and the generated file define the same function.Use named types to distinguish values of the same underlying type — wire enforces one provider per type; named types like
type DSN stringlet you havePrimaryDSNandReplicaDSNcoexist.Keep library provider sets minimal and backward-compatible — adding new required inputs breaks downstream injectors; removing outputs does too. Introduce only newly-created types in the same release.
Return
(T, func(), error)from cleanup providers and let wire chain them — wire generates the correct reverse-order cleanup and handles partial failures (if construction fails midway, only already-built cleanups run).Keep injector files focused — one function per file, one package import at a time. Fat injectors with dozens of
wire.Buildarguments are hard to reason about; delegate to per-package sets.
Common Mistakes
| Mistake | Fix |
|---|---|
Editing wire_gen.go manually | Never edit it. Change providers or injectors and re-run wire ./.... |
Missing //go:build wireinject | Add the tag as the very first line of every injector file. |
Two providers returning *sql.DB | Wrap with a named struct type: type PrimaryDB struct { *sql.DB } — Wire does not distinguish pointer type aliases. |
Injecting an interface without wire.Bind | Add wire.Bind(new(MyInterface), new(*MyImpl)) to the provider set. |
Forgetting to re-run wire ./... after changes | Run wire before go build; add it to go generate or a Makefile target. |
Calling cleanup() without guarding for nil | Wire returns nil cleanup on construction error; guard with if cleanup != nil { defer cleanup() }. |
Testing
Wire generates plain Go constructors, so unit tests use manual injection — no container to clone or reset. For testing patterns (test injectors swapping real providers for fakes, CI stale-check for wire_gen.go), see testing.md.
Further Reading
advanced.md — cleanup chains, multiple injectors, set nesting, error catalogue, codegen flags, quick reference
recipes.md — HTTP server, multi-injector build, cleanup-heavy graph, CLI embedding
testing.md — test injectors, fake bindings, CI stale check
Cross-References
→ See
samber/cc-skills-golang@golang-dependency-injectionskill for DI concepts and library comparison→ See
samber/cc-skills-golang@golang-uber-digskill for runtime reflection-based DI without lifecycle→ See
samber/cc-skills-golang@golang-uber-fxskill for runtime DI with lifecycle hooks, modules, and signal-aware Run()→ See
samber/cc-skills-golang@golang-samber-doskill for generics-based DI without reflection→ See
samber/cc-skills-golang@golang-structs-interfacesskill for interface design patterns→ See
samber/cc-skills-golang@golang-testingskill for general testing patterns
If you encounter a bug or unexpected behavior in google/wire, open an issue at https://github.com/google/wire/issues.
Files
- ATTRIBUTION.mdAdded by AutoGineer911 B
- LICENSELicence file added by AutoGineer1.0 KiB
- SKILL.md10.9 KiB
- evals/evals.json15.6 KiB
- references/advanced.md7.3 KiB
- references/recipes.md6.9 KiB
- references/testing.md5.5 KiB
references/advanced.md
Advanced — google/wire
Detail topics referenced from SKILL.md. Each section is self-contained.
Table of Contents
Cleanup Chains
Multiple Injectors in One Package
wire.NewSet Nesting Strategies
wire:"-"Exclusion TagCommon Codegen Errors
Codegen Flags
panic(wire.Build(...))Alternate SyntaxAccepting External Values as Injector Arguments
Quick Reference
Cleanup Chains
When a provider returns (T, func(), error), Wire adds the cleanup to a chain. The generated injector runs cleanups in reverse construction order: the last-built dependant is cleaned up first, ensuring dependants are torn down before their dependencies.
// Provider with cleanup
func NewDB(cfg *Config) (*sql.DB, func(), error) {
db, err := sql.Open("postgres", string(cfg.DSN))
if err != nil { return nil, nil, err }
return db, func() { db.Close() }, nil
}
func NewCache(cfg *Config) (*redis.Client, func(), error) {
c := redis.NewClient(&redis.Options{Addr: cfg.CacheAddr})
return c, func() { c.Close() }, nil
}Wire generates something like:
func InitApp() (*App, func(), error) {
cfg := NewConfig()
db, dbCleanup, err := NewDB(cfg)
if err != nil { return nil, nil, err }
cache, cacheCleanup, err := NewCache(cfg)
if err != nil {
dbCleanup() // already-built cleanups run on partial failure
return nil, nil, err
}
app := NewApp(db, cache)
return app, func() {
cacheCleanup() // reverse order
dbCleanup()
}, nil
}Caller pattern — guard against nil cleanup on construction failure:
app, cleanup, err := InitApp()
if err != nil { log.Fatal(err) }
defer cleanup()Wire always returns a non-nil cleanup function when construction succeeds. If construction fails midway, the returned cleanup is nil — guard before calling.
Multiple Injectors in One Package
A package can contain multiple injector functions. Each must live in a file with //go:build wireinject. All generated functions land in wire_gen.go in the same package.
//go:build wireinject
package main
// Production injector
func InitProdApp() (*App, func(), error) {
wire.Build(ProdSet, NewApp)
return nil, nil, nil
}
// Development injector with debug providers
func InitDevApp() (*App, func(), error) {
wire.Build(DevSet, NewApp)
return nil, nil, nil
}Select at runtime with a flag, or at build time with separate //go:build prod / //go:build !prod constraints on the injector files.
wire.NewSet Nesting Strategies
Sets can contain other sets, building a hierarchy that mirrors your package structure.
// pkg/config/wire.go
var ConfigSet = wire.NewSet(NewConfig)
// pkg/infra/wire.go
var InfraSet = wire.NewSet(
config.ConfigSet, // embed upstream set
NewDB,
NewCache,
)
// pkg/service/wire.go
var ServiceSet = wire.NewSet(
NewUserService,
wire.Bind(new(UserStore), new(*UserRepo)),
)
// wire.go (injector)
wire.Build(infra.InfraSet, service.ServiceSet, NewApp)Library set stability rules (from upstream best practices):
Safe: replace one provider with another that has the same or fewer inputs, in the same release.
Safe: introduce a brand-new output type not previously provided.
Breaking: add a new required input to a provider — downstream injectors cannot satisfy it.
Breaking: remove a provided output type — downstream injectors that depend on it fail.
Breaking: add a type that the injector already provides — Wire reports a duplicate.
wire:"-" Exclusion Tag
Exclude a struct field from wire.Struct injection by tagging it:
type Server struct {
Logger *zap.Logger
DB *sql.DB
mu sync.Mutex `wire:"-"` // unexported — auto-excluded
Timeout time.Duration `wire:"-"` // exported but opt-out
}
wire.Struct(new(Server), "*") // injects Logger and DB; skips mu and TimeoutUnexported fields are always skipped regardless of the tag.
Common Codegen Errors
| Error message | Root cause | Fix |
|---|---|---|
no provider found for TYPE | A dependency is not provided by any set in wire.Build | Add the missing provider or set |
multiple bindings for TYPE | Two providers return the same type | Use named types or remove the duplicate |
argument N has no provider for TYPE | An interface is requested but no wire.Bind maps to it | Add wire.Bind(new(Iface), new(*Impl)) to a set |
cycle detected | A → B → A circular dependency | Break the cycle by introducing an interface or factory |
wire.Build used outside of injector function | wire.Build called from a non-injector function | Only call wire.Build inside functions with the build tag |
| duplicate symbol / redeclared in this block | Injector file is missing //go:build wireinject | Add the build tag as the first line |
Codegen Flags
# Specify output file prefix (default: wire_gen)
wire -output_file_prefix=init gen ./cmd/server
# Apply build tags during generation
wire -tags=integration gen ./...
# Prepend a header file (e.g., license comment) to generated output
wire -header_file=hack/boilerplate.go.txt gen ./...panic(wire.Build(...)) Alternate Syntax
Wire accepts either a dummy return or a panic call as the injector body. The panic form avoids writing zero-value returns for complex types:
// Preferred when return types are complex or error-prone to zero-initialize
func InitApp(ctx context.Context) (*App, func(), error) {
panic(wire.Build(AppSet))
}Wire detects both forms and replaces the body during codegen. The panic is never reached in the compiled binary — only the generated wire_gen.go version is compiled.
Accepting External Values as Injector Arguments
When a value is constructed before wire runs (e.g., parsed flags, an http.Client from a test), pass it as a parameter to the injector rather than providing it from within the graph:
//go:build wireinject
func InitApp(cfg *Config) (*App, func(), error) {
wire.Build(InfraSet, ServiceSet, NewApp)
return nil, nil, nil
}
// main.go
cfg := parseFlags()
app, cleanup, err := InitApp(cfg)Wire treats injector parameters as pre-built providers — they satisfy dependencies without needing a wire.NewSet entry.
Quick Reference
| Symbol | Purpose |
|---|---|
wire.NewSet(providers...) | Group providers into a reusable set |
wire.Build(sets...) | Declare injector body (codegen replaces it) |
wire.Bind(new(Iface), new(*Concrete)) | Bind interface to concrete type |
wire.Struct(new(T), "Field", ...) | Inject struct fields from the graph |
wire.Struct(new(T), "*") | Inject all non-excluded fields |
wire.Value(expr) | Bind a constant expression (no fn calls/channels) |
wire.InterfaceValue(new(I), value) | Bind a value to an interface type |
wire.FieldsOf(new(T), "Field", ...) | Promote struct fields as individual graph nodes |
//go:build wireinject | Build tag: exclude injector stub from binary |
wire_gen.go | Generated output — commit, never edit |
wire ./... | Regenerate all injectors in the module |
wire check ./... | Validate graph without regenerating |
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-google-wire · 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-google-wire.zip