Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
314 changes: 205 additions & 109 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,160 +20,256 @@
No runtime container, no reflection, no hidden magic - just fast, explicit initialization.
</p>

> [!NOTE]
> This is a maintained fork of `google/wire`.
> The original project is no longer actively maintained.
> This fork preserves Wire’s API and behavior while focusing on:
>
> - 8-10x+ Faster compile times
> - Improved generator determinism
> - Better documentation and developer ergonomics (wire watch etc.)
>
> Existing Wire codebases should work without modification.

Wire is a code generation tool that automates connecting components using
[dependency injection][]. Dependencies between components are represented in
Wire as function parameters, encouraging explicit initialization instead of
global variables. Because Wire operates without runtime state or reflection,
code written to be used with Wire is useful even for hand-written
initialization.

For an overview, see the [introductory blog post](https://blog.golang.org/wire).

[dependency injection]: https://en.wikipedia.org/wiki/Dependency_injection
[godoc]: https://godoc.org/github.com/goforj/wire
**GoForj Wire is a maintained fork of [Google Wire](https://github.com/google/wire).**
Google Wire is archived. This fork adds caching for faster repeat runs, watch
mode, and support for `go tool wire`. You can use it in any Go project.

## Installing
Wire connects your components by generating the Go code that calls their
constructors. You write normal functions and declare which ones Wire can use.
Wire works out the call order and passes each dependency where it's needed.
The result is code you can read, debug, or write by hand.

```sh
go install github.com/goforj/wire/cmd/wire@latest
```
Already using Google Wire? This fork generated code **84-109x faster on unchanged
runs** in our larger test apps with warm caches. See
[what the fork adds](#why-goforj-wire) and the [measured tradeoffs](#generation-performance).

Ensure `$GOPATH/bin` is in your `$PATH`.
## Why would I use Wire?

### Project-owned tools and workspaces
**Keep writing constructors. Stop maintaining the code that connects them.**

To manage Wire as a project tool dependency, add it to the module and opt into
the tool-based generation command:
An orders API and a background worker might share a service, which needs a
repository, an inventory client, and a mailer. Wiring that by hand is straightforward:

```sh
go get -tool github.com/goforj/wire/cmd/wire
go tool wire gen -generate_command="go tool wire" ./...
```go
// InitializeApp connects the API and worker to the same order service.
func InitializeApp(cfg Config, db *sql.DB, logger *slog.Logger) *App {
repo := NewOrderRepository(db)
inventory := NewInventoryClient(cfg)
mailer := NewMailer(cfg)
orders := NewOrderService(repo, inventory, mailer)
handler := NewOrderHandler(orders, logger)
worker := NewOrderWorker(orders, logger)
return NewApp(handler, worker)
}
```

Wire then emits a directive that keeps this choice on subsequent runs:
There is nothing wrong with this code. The work comes later: a constructor
changes, a dependency needs its own dependencies, or another command needs a
different combination of the same services. You update each setup function and
keep the call order, arguments, and shared instances straight.

With Wire, you list the constructors once and declare what you want to build:

```go
//go:generate go tool wire gen -generate_command "go tool wire"
//go:build wireinject

package orders

import (
"database/sql"
"log/slog"

"github.com/goforj/wire"
)

// AppSet lists the constructors available to the application.
var AppSet = wire.NewSet(
NewOrderRepository,
NewInventoryClient,
NewMailer,
NewOrderService,
NewOrderHandler,
NewOrderWorker,
NewApp,
)

// InitializeApp connects the API and worker to the same order service.
func InitializeApp(cfg Config, db *sql.DB, logger *slog.Logger) *App {
wire.Build(AppSet)
return nil
}
```

This works with `go generate` in a Go workspace without the default command's
`-mod=mod` flag. The project must declare the Wire tool dependency. The tool
directive requires Go 1.24 or newer; the selected Wire version's own Go minimum
also applies.
Run `wire gen`. Wire writes `wire_gen.go` with the same constructor calls as
the manual version above. The build tag keeps the stub out of normal builds.
`Config`, `App`, and the constructors are your application's ordinary Go code;
Wire doesn't require a container or special constructor signatures.

`-generate_command` is available on `gen`, `diff`, and `watch`. Its value is the
command prefix that invokes Wire; Wire appends `gen`, the override itself, and
any build tags. Custom wrappers must forward those arguments. Commands follow
Go generate's argument rules, including Go-style double-quoted arguments and
environment-variable expansion. They are not interpreted by a shell.
### When a dependency changes

Omitting the flag preserves the existing `go run -mod=mod` directive and generated
output. To restore that default, run `wire gen` without the override.
Suppose orders now need an audit log. You add a constructor and change the
service's signature:

## Compatibility with google/wire
```go
// NewAuditLog creates an audit log backed by the application's database.
func NewAuditLog(db *sql.DB) *AuditLog {
return &AuditLog{db: db}
}

Wire remains compatible with codebases that import `github.com/google/wire`.
The generator emits a `//go:generate` directive pointing at this fork so
`go generate` consistently uses the maintained toolchain.
// NewOrderService supplies the dependencies needed to process orders.
func NewOrderService(
repo *OrderRepository,
inventory *InventoryClient,
mailer *Mailer,
audit *AuditLog,
) *OrderService {
return &OrderService{repo: repo, inventory: inventory, mailer: mailer, audit: audit}
}
```

To keep existing imports unchanged:
Add `NewAuditLog` to `AppSet` and rerun Wire. You don't edit the injector's
signature or hand-update its constructor calls. The generated code now includes:

```sh
go mod edit -replace=github.com/google/wire=github.com/goforj/wire@latest
```go
orderRepository := NewOrderRepository(db)
inventoryClient := NewInventoryClient(cfg)
mailer := NewMailer(cfg)
auditLog := NewAuditLog(db)
orderService := NewOrderService(orderRepository, inventoryClient, mailer, auditLog)
```

## How Wire Works (in 60 seconds)
The handler and worker still receive the same `orderService`. If you forget to
register `NewAuditLog`, generation fails with a missing-provider error and shows
the dependency chain that needs it. You find out before starting the app.

Wire is a **compile-time dependency injection** tool. Instead of building a runtime
container, Wire generates ordinary Go code that explicitly initializes your
application.
**You still own the design.** You choose constructors, interfaces, and which
values enter the injector. Wire follows those choices; it doesn't discover
services at runtime. Tests can call `NewOrderService` directly with test
dependencies, without running Wire.

There are only two concepts to learn:
If your app has a few constructors and one setup function, hand wiring is
probably enough. Wire earns its place when keeping those calls up to date across
a growing application becomes repetitive. Start with the
[working tutorial](_tutorial/README.md) to try it.

### Providers
## Why GoForj Wire?

A provider is a normal Go function that produces a value (and optionally an error).
**Fast reruns, even with hundreds of dependencies.** In our test apps, unchanged
generation was **84-109x faster than Google Wire** with warm caches. After a
function body edit, it was **18-19x faster**. See the [benchmarks](#generation-performance)
for project sizes, timings, and first-run costs.

```go
func NewDB(cfg Config) (*sql.DB, error) {
// ...
}
````
Your providers, `wire.Build` calls, provider sets, interface bindings, and
cleanup functions work the same way as in Google Wire. Here's what this fork adds:

Dependencies are expressed as function parameters - no globals, no hidden state.
| Feature | How it helps |
| --- | --- |
| **Faster repeat runs** | Wire caches package information and generated code so it can skip work on later runs. See the [benchmark results](#generation-performance). |
| **Watch mode** | Run `wire watch ./...` to regenerate when Go files change. It uses filesystem notifications, with polling as a fallback. |
| **Pin Wire in your project** | Use `-generate_command="go tool wire"` so `go generate` uses the Wire version in your `go.mod`. This also works in Go workspaces, where the default command's `-mod=mod` flag causes problems. |
| **Workspace fixes** | Change a constructor in another module, rerun Wire, and its generated calls update too. This works with `go.work` and local `replace` directives, even if you keep Wire in a separate tools module. Fixed in [v1.3.0](https://github.com/goforj/wire/releases/tag/v1.3.0). |
| **Cache and profiling tools** | Use `wire cache` to find the cache directory or `wire cache clear` to clear it. For slow runs, `-timings`, `-cpuprofile`, `-memprofile`, and `-trace` help you find where the time goes. |

### Injectors
The generated code still calls your constructors directly, with no runtime
container or reflection.

An injector is a stub function whose body calls `wire.Build(...)`.
Wire replaces this stub with generated code that calls providers in dependency
order.
### Generation performance

```go
func InitializeApp(cfg Config) (*App, error) {
wire.Build(AppSet)
return nil, nil
}
We compared GoForj Wire v1.3.0 with Google Wire at
[`9c25c90`](https://github.com/google/wire/commit/9c25c9016f6825302537c4efdd5e897976f9c826).
For a test app with **117 local packages and 342 external packages**, these were
the median times across five runs:

| Change | Google Wire | GoForj Wire | Speedup |
| --- | ---: | ---: | ---: |
| Unchanged | 1,582.0 ms | 18.9 ms | 83.7x |
| Function body edit | 1,580.0 ms | 87.3 ms | 18.1x |
| Constructor argument added | 1,604.8 ms | 262.6 ms | 6.1x |
| Import added | 1,567.2 ms | 267.8 ms | 5.9x |

The app with 42 local and 342 external packages reached **109x** on unchanged
runs. The project with 1,016 local packages and few external dependencies saw
**9x**. The kind of dependencies matters as well as the package count.

These measurements use Go 1.25.0 on an Apple M4 Max, with Go's dependency build
data already cached. Each edit starts from a warmed Wire cache. They time
**code generation**, not `go build` or your running application.

First runs can be slower while Wire builds its caches. The workspace fix also
adds some checking: in the large local workspace, a cached rerun went from
59.9 to 62.9 ms. With opt-in content hashing, it went from 62.1 to 72.6 ms because
Wire now reads sibling files that older releases skipped.

We also compared v1.2.0 and v1.3.0 across Go 1.25, 1.26, and 1.27, checked cold
caches, and compiled the generated code after edits. See the
[full results and raw measurements](docs/performance.md) for the release
comparison, cache settings, and tradeoffs.

To run the repository's application benchmark:

```sh
bash scripts/import-benchmarks.sh scenarios
```

The generated output is plain Go - readable, debuggable, and fast.
That runner prints three-run medians and includes a cold Go cache case. The
five-run comparison above warms Go's dependency build data first; the full
report explains both setups. For the smaller import-only test, run
`bash scripts/import-benchmarks.sh table`.

## Minimal Example
## Installing

```go
// providers.go
type Message string
**Wire v1.3.0 requires Go 1.25 or later.** Check your local and CI Go versions
before upgrading.

func ProvideMessage() Message {
return "hello wire"
}
```sh
go install github.com/goforj/wire/cmd/wire@latest
```

type Greeter struct {
Msg Message
}
Ensure `$GOPATH/bin` is in your `$PATH`.

func NewGreeter(msg Message) *Greeter {
return &Greeter{Msg: msg}
}
### Pinning Wire in your project

var GreeterSet = wire.NewSet(
ProvideMessage,
NewGreeter,
)
To pin Wire's version in your `go.mod`, add it as a Go tool:

```sh
go get -tool github.com/goforj/wire/cmd/wire@v1.3.0
go tool wire gen -generate_command="go tool wire" ./...
```

```go
// wire.go
//go:build wireinject
Wire adds this command to the generated file:

func InitializeGreeter() *Greeter {
wire.Build(GreeterSet)
return nil
}
```go
//go:generate go tool wire gen -generate_command "go tool wire"
```

Generate the injector:
Now `go generate` uses `go tool wire` too. This works inside a `go.work` workspace,
where the default `go run -mod=mod` command would fail. Keep the tool dependency
in your `go.mod`. Wire v1.3.0 requires Go 1.25 or later.

You can use `-generate_command` with `gen`, `diff`, and `watch`. Wire adds `gen`,
the `-generate_command` flag, and any build tags to the command you supply. If
you use a wrapper script, make sure it forwards those arguments. Quoting and
environment variables follow `go generate` rules; shell operators aren't supported.

Without the flag, Wire generates its usual `go run -mod=mod` directive. Run
`wire gen` without the flag to switch back to that default.

## Moving from Google Wire

Keep your existing provider sets and injector stubs. The generator accepts both
`github.com/google/wire` and `github.com/goforj/wire` imports. If your code still
imports Google Wire, keep that dependency in your `go.mod`. You don't need a
`replace` directive to use this generator.

Install the fork, then run it in your application's module:

```sh
wire
go install github.com/goforj/wire/cmd/wire@v1.3.0
wire gen ./...
```

Wire produces `wire_gen.go` containing explicit initialization code with no
runtime dependency on Wire.
Review and commit the regenerated files. Their `//go:generate` command now points
to `github.com/goforj/wire/cmd/wire`, so you'll see a diff even if your providers
haven't changed. To pin the generator version or use it in a `go.work` workspace,
follow [Pinning Wire in your project](#pinning-wire-in-your-project).

New projects can import `github.com/goforj/wire` directly. Its module and generator
require Go 1.25. The generated initialization code has no runtime dependency on
Wire.

## Running generation manually

Use `make generate` to run `wire gen` across the repository. The CLI remains useful for targeted generation:
Use `make generate` to run `wire gen` across this repository. In your own project, run:

```sh
wire
Expand All @@ -193,7 +289,7 @@ wire watch
wire watch ./...
```

Detects the package root automatically and uses native filesystem notifications when available (with a polling fallback).
Wire finds the package root automatically. It uses filesystem notifications when available and falls back to polling.

## Design Guidance

Expand Down Expand Up @@ -230,9 +326,9 @@ initialization is often simpler and clearer.

## Project Status

This fork tracks the original Wire feature set while prioritizing performance,
determinism, and documentation. Compatibility with existing Wire codebases is a
core goal.
We maintain this fork so existing Wire projects can keep using it with current
Go tooling. See the [release notes](https://github.com/goforj/wire/releases) for
changes and required Go versions.

## Community

Expand Down
13 changes: 13 additions & 0 deletions docs/benchmarks/cold-go-cache.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
profile,version,trial,ms
local-high,v1.2.0,0,7487.182
local-high,v1.3.0,0,7627.225
local-high,v1.3.0,1,6945.317
local-high,v1.2.0,1,7283.108
local-high,v1.2.0,2,7256.294
local-high,v1.3.0,2,7435.483
external-high,v1.2.0,0,10548.371
external-high,v1.3.0,0,10133.144
external-high,v1.3.0,1,10281.93
external-high,v1.2.0,1,10156.272
external-high,v1.2.0,2,10136.897
external-high,v1.3.0,2,10218.191
Loading
Loading