After years of building large-scale Go projects, I’ve always believed that Go engineering optimization should be evolution with no tradeoffs, not compromise.
Dependency Injection is a foundational piece for medium/large Go systems, monorepos and microservices. However, the two mainstream Go DI solutions both have fatal flaws that force developers into an awkward dilemma:
Uber Fx: Great DX, terrible runtime overhead
- Clean, elegant API and excellent modular design
- Runtime reflection heavy: slow startup, non-trivial runtime overhead, larger binary size
- All dependency errors (missing providers, circular dependencies, type mismatch) only panic at runtime, not compile time
- Too risky for high-performance infrastructure and core online services
Google Wire: Perfect performance, terrible developer experience
- 100% compile-time code generation, zero reflection, zero runtime dependency
- Guaranteed compile-time safety, fully aligned with Go’s static philosophy
- Verbose, counter-intuitive API, mandatory
wire.NewSetboilerplate - Only supports constant injection, no runtime variable supply
- Flat module structure only, no nested modularity for large projects
- Unreadable error logs, extremely painful dependency troubleshooting
For a long time, Go developers had to choose: either good DX with performance risk, or perfect performance with poor productivity.
So I built dig — a pure Go compile-time DI library that eliminates all tradeoffs.
dig Core Positioning
dig = Wire’s compile-time safety & zero overhead + Fx’s elegant API & modularity + exclusive safety checks
It abides strictly by Go’s design philosophy: simple, transparent, predictable, no magic.
- All dependency graph parsing, circular detection and unused component validation happen at compile-time (go generate)
- Outputs pure handwritten-style Go code, zero runtime reflection, zero framework dependency
- Final binary performance is identical to manual initialization
Why dig Is Better Than Existing DI Tools
1. Fx-style Minimal & Intuitive API
Only 5 core APIs, near-zero learning curve, completely gets rid of Wire’s verbose boilerplate:
Provide: Register constructor functionsSupply: Inject any value (constants, global variables, runtime values — Wire’s biggest limitation fixed)Invoke: Execute startup hooks after all dependencies are readyModule: Nested modular combination for monorepo architectureBuild: Assemble the full dependency graph
Minimal usage example:
//go:build digen
func InitApp() func(context.Context) error {
return dig.Build(
dig.Provide(NewConfig),
dig.Provide(NewDB),
dig.Supply(DefaultTimeout),
dig.Invoke(func(srv *Server) error { return srv.Run() }),
)
}
2. Exclusive Closure Safety Check (Unique to dig)
Both Fx and Wire suffer from implicit closure capture bugs, which cause intermittent runtime panics that are extremely hard to reproduce.
dig enforces compile-time closure validation: it prohibits capturing local function variables in builder functions, only allowing package-level variables and literals. This eliminates implicit dependency bugs at the syntax level.
3. Complete Observability & Refined Strategy
- Compile-time: 3-level unused component strategy (error/ignore/drop), automatic dead code cleanup
- Runtime: Plugable global logging interface, natively compatible with zap/logrus
- Error prompt: Hierarchical dependency chain logs, precise positioning without obscure stack traces
4. Modern Go Feature Support
- Native generic constructor & generic injection support
- Nested module management for large monorepo projects
- Mixed compile-tag selection and runtime conditional injection
- Built-in wrapper type resolution to avoid identical type conflicts
Feature Comparison: dig vs Wire vs Fx
| Capability | dig | Google Wire | Uber Fx |
|---|---|---|---|
| Compile-time code generation | |||
| Zero runtime reflection | |||
| Zero runtime framework dependency | |||
| Compile-time error capture | |||
| Clean Fx-style API | |||
| Closure safety validation | |||
| Arbitrary runtime value injection | |||
| Unused component optimization |
Philosophy: What Go DI Should Really Be
Most Go developers resist DI not because DI is unnecessary, but because they reject Java-style heavy, black-box framework magic.
- Fx breaks predictability: runtime reflection brings uncontrollable runtime uncertainty.
- Wire breaks simplicity: rigid syntax damages development efficiency for large projects.
dig’s goal is simple: no more tradeoffs.
It keeps Go’s original simplicity and transparency, while solving the chaos of manual initialization in multi-layer, multi-module projects:
- No global variable flooding
- No scattered initialization logic
- No chain reaction modification risks
- Full mock capability for unit testing
Installation & Quick Start
Current version: v1.0.10 (Go 1.21+, MIT license, free for commercial use)
go get github.com/shanjunmei/dig@v1.0.10
go install github.com/shanjunmei/dig/cmd/digen@latest
Full documentation, complete examples and migration guides are available in the repo.
Welcome Feedback & Contribution
dig is built to fill the last gap of Go’s DI ecosystem. If you are tired of Fx’s runtime overhead and Wire’s cumbersome syntax, give dig a try.
Stars, issues and PRs are all welcome! Let’s build a more native, efficient and developer-friendly DI solution for Go.