grafana/scripts/go/wirecheck
..
cmd/wirechecker
testdata
.gitignore
README.md
analyzer.go
go.mod
go.sum
wire_parser.go
wirecheck.sh

README.md

Wire Checker

A golangci-lint linter that detects direct dependency method calls in wire provider functions, helping maintain proper dependency injection patterns.

Overview

Wire Checker analyzes Go code to find provider functions that directly call methods on their dependencies, which can lead to tight coupling and violate dependency injection principles.

Usage

Lint all files

make lint-go

VS Code Integration

Configure VS Code to use the custom golangci-lint with wirecheck:

{
  "go.lintTool": "golangci-lint-v2",
  "go.lintFlags": ["--config=${workspaceRoot}/.golangci.yml"],
  "go.alternateTools": {
    "golangci-lint-v2": "${workspaceRoot}/scripts/go/golangci-lint/vscode-wrapper.sh"
  },
  "go.lintOnSave": "package"
}

Example

Here is an example of a wire provider that calls a method on one of its dependencies. This is a problem because it breaks the dskit module/service startup guarantees. The database connection happens during wire initialization instead of during the service's starting phase, which can cause services to start in the wrong order or fail if dependencies aren't healthy and ready to accept requests.

func ProvideUserService(db *Database) (*UserService, error) {
    // BAD: Direct method calls on dependencies
    if err := db.Connect(context.Background()); err != nil {    // ❌ Will be detected
        return nil, err
    }

    return &UserService{db: db}, nil
}

Wire Checker will report:

provide.go:95:2: ProvideUserService() directly calls db.Connect() in wire provider function

Good Practice

Use a dskit service starting function for initialization.


import "github.com/grafana/dskit/services"

func ProvideUserService(db *Database) *UserService {
  service := &UserService{db: db}
  service.NamedService = services.NewBasicService(service.starting, service.running, nil)
  return service
}

type UserService struct {
    db *Database
    services.NamedService
}

func (s *UserService) starting(ctx context.Context) error {
  return s.db.Connect(ctx)
}

func (s *UserService) running(ctx context.Context) error {
  <- ctx.Done()
  return nil
}

Configuration

Configured in .golangci.yml:

linters:
  enable:
    - wirecheck
settings:
  custom:
    wirecheck:
      type: module
      description: Check for direct dependency method calls in wire provider functions
      settings:
        wire-gen: ./pkg/server/wire_gen.go
        recursive: true