跪拜 Guibai
← Back to the summary

Google Wire Is Dead and Uber Fx Panics at Boot — A Compile-Time DI Library Steps In

In August 2025, Google Wire was archived by its official maintainers. The project once hailed as the "compile-time dependency injection standard" is dead.

A friend in a group chat immediately blew up: "I just finished writing my entire project with Wire, and you're telling me it's archived?"

Calm down. Wire still works, but don't expect new features. The key point is, it wasn't that great to use in the first place.

Three moments that make Wire maddening

First breakdown: return nil, nil

func InitApp() (*App, error) {
    wire.Build(NewDB, NewService, NewHandler)
    return nil, nil  // This line never executes
}

The first time you see this code, are you also confused? It declares a return of *App, but the result is return nil. Why? Because wire.Build is a compile-time marker and doesn't run at runtime. But Go's syntax requires a return value, so you have to write this line of nonsense.

This isn't a bug. It's a feature. An anti-human feature.

Second breakdown: No startup hooks

Wire does only one thing: constructs your objects. Then what? Starting services, connecting to databases, registering routes... you have to write all of that yourself.

So your main.go turns into this:

func main() {
    app, _ := InitApp()
    app.DB.Connect()
    app.Server.Start()
    app.Router.Register()
    app.Cache.Warmup()
    app.Metrics.Export()
    // Miss one, and it blows up in production
}

Every time you add a new component, you have to manually add a line. Forget one? No one reminds you; it goes straight to production and explodes.

Third breakdown: No generics support

Go 1.18 has been out for three years, and Wire's support for generics is still "manual wrapping."

// You want to write this?
dig.Provide(NewStore[int])  // Native generics, write it directly

// Wire requires this:
func provideStoreInt() *Store[int] { return NewStore[int]() }
var Set = wire.NewSet(provideStoreInt)

Write a wrapper function for every type. The code bloats like a tourist attraction during a national holiday.

So Fx is perfect? Think again

Fx's API is indeed beautiful, one dependency per line, pleasing to the eye. But at startup, you never know what will happen:

panic: missing dependency: *DB (did you forget to provide it?)

Compilation passed, tests ran, and it panics on startup.

Why? Fx relies on runtime reflection. Dependencies are not checked at compile time; all errors wait for you at startup.

The larger the project, the slower the startup. Because it has to reflect and parse the entire dependency tree every time. Change one line of code locally, wait half a day for a restart, and development efficiency is cut in half.

Interface return + concrete injection = time bomb

fx.Provide(func() interface{} { return &DB{} })
fx.Invoke(func(db *DB) { ... })
// Startup panic: missing dependency

Type mismatch? Can't see it at compile time; it crashes directly on startup.

This "compiles but panics on startup" experience, anyone who has used it understands.

So what to use? Check out dig

dig is a compile-time DI framework. Its syntax is like Fx, its safety is like Wire, but without Wire's problems.

//go:build digen
func InitApp() func(context.Context) error {
    return dig.Build(
        dig.Provide(NewConfig),      // Normal constructor
        dig.Provide(NewDB),
        dig.Supply(DefaultTimeout),  // Inject value directly, no wrapping
        dig.Provide(func(t Timeout) *Server { return NewServer(t) }),
        dig.Invoke(func(srv *Server) error { return srv.Run() }),
    )
}

Note: No return nil, nil.

No reflection, all compile-time code generation.

How does dig solve these pain points?

1. No dummy placeholders

dig.Build directly returns an executable function:

func InitApp() func(context.Context) error {
    return dig.Build(...)  // Returns a function, not a dummy placeholder
}

Intuitive, even a beginner can understand it.

2. Native generics support

dig.Provide(NewStore[int])   // Pass generic type directly
dig.Provide(NewStore[string])

No need to write wrapper functions; the code is clean.

3. Built-in Invoke, no manual maintenance checklist

dig.Invoke(func(db *DB) error { return db.Ping() })
dig.Invoke(func(srv *Server) error { return srv.Start() })

Automatically executes when dependencies are ready. Add one line of Invoke for a new component, and you'll never forget.

4. Closure capture checking

Wrote an inline closure in Provide and accidentally captured a local variable? go generate reports an error directly:

❌ cannot capture local variable "t"

Wire silently passes this, then generates code that fails to compile, and you can't even see where the problem is.

5. Compile-time checking

go generate ./...
# Missing dependency? Circular reference? Type conflict? Errors are reported right here, no need to wait until runtime

Compile-time vs. Runtime: The essential difference

Dimension dig / Wire Fx
Dependency resolution timing go generate phase Program startup (reflection)
Dependency error discovery Errors reported during code generation, won't even compile Runtime panic, crashes on startup
Runtime overhead Pure Go function calls, no extra overhead Reflection parses types, builds container, extra CPU + memory overhead
Binary size Pure generated Go code, no extra metadata Carries type info and reflection metadata, larger size

dig and Wire are compile-time solutions:

Fx is a runtime solution:

Technical comparison

Note: ✅ indicates feature support, ❌ indicates no support, ⚠️ indicates support with limitations or costs, N/A indicates not applicable.

Dimension dig Wire Fx
Positioning Compile-time generation Compile-time generation Runtime reflection
wire.Build dummy placeholder ❌ Not needed ❌ Must write return nil ❌ Not needed
Generic Provider ✅ Native support ❌ Not supported (manual wrapping required) ⚠️ Reflection support, with overhead
Startup hooks (Invoke) ✅ Built-in ❌ None (manual maintenance) ✅ Built-in
Closure capture check ✅ Mandatory check ❌ No check N/A
Module definition method Functions, can pass parameters Package-level variables, inflexible Functions, can pass parameters
Official maintenance status ✅ Actively maintained ❌ Archived (2025.8) ✅ Actively maintained

Is the migration cost high?

No.

go get github.com/shanjunmei/[email protected]
go install github.com/shanjunmei/dig/cmd/digen@latest

Final words

Google archived Wire, and Fx is still holding on with reflection.

If you:

dig is worth 5 minutes of your time to try.

github.com/shanjunmei/dig