
golang-dependency-injection
Comprehensive guide for dependency injection (DI) in Golang. Covers why DI matters (testability, loose coupling, separation of concerns, lifecycle management), manual constructor injection, and DI library comparison (google/wire, uber-go/dig, uber-go/fx, samber/do). Use this skill when designing service architecture, setting up dependency injection, refactoring tightly coupled code, managing singletons or service factories, or when the user asks about inversion of control, service containers, or wiring dependencies in Go. For a specific DI library, → See samber/cc-skills-golang@golang-google-wire, samber/cc-skills-golang@golang-uber-dig, samber/cc-skills-golang@golang-uber-fx, or samber/cc-skills-golang@golang-samber-do skills.
- Installs
- 39.6K
- Last 24h
- +156
- Trending rank
- #1451
- Security
- Pass
Install
Works with every agent the skills CLI supports.
npx skills add https://github.com/samber/cc-skills-golang --skill golang-dependency-injection- 01
Install
Run the command in your project. The CLI asks which agents to install for.
- 02
Describe the task
Ask your agent for something this skill covers. It loads golang-dependency-injection automatically when relevant.
- 03
Review the result
The agent follows the SKILL.md steps, scripts and references bundled with the skill.
Security audits
Independent scans of the skill's instructions and bundled files.
SKILL.md
The instructions your agent reads when the skill is triggered.
Persona: You are a Go software architect. You guide teams toward testable, loosely coupled designs — you choose the simplest DI approach that solves the problem, and you never over-engineer.
Orchestration mode: Fan out the three sub-agents described in Refactor mode (global/init discovery, concrete-dependency mapping, service-locator detection) when refactoring a large coupled codebase toward dependency injection, and consolidate into one migration plan. On Claude Code, use ultracode to opt into multi-agent orchestration explicitly.
Modes:
- Design mode (new project, new service, or adding a service to an existing DI setup): assess the existing dependency graph and lifecycle needs; recommend manual injection or a library from the decision table; then generate the wiring code.
- Refactor mode (existing coupled code): use up to 3 parallel sub-agents — Agent 1 identifies global variables and
init()service setup, Agent 2 maps concrete type dependencies that should become interfaces, Agent 3 locates service-locator anti-patterns (container passed as argument) — then consolidate findings and propose a migration plan.
Community default. A company skill that explicitly supersedes
samber/cc-skills-golang@golang-dependency-injectionskill takes precedence.
Dependency Injection in Go
Dependency injection (DI) means passing dependencies to a component rather than having it create or find them. In Go, this is how you build testable, loosely coupled applications — your services declare what they need, and the caller (or container) provides it.
This skill is not exhaustive. When using a DI library (google/wire, uber-go/dig, uber-go/fx, samber/do), refer to the library's official documentation and code examples for current API signatures.
For interface-based design foundations (accept interfaces, return structs), see the samber/cc-skills-golang@golang-structs-interfaces skill.
Best Practices Summary
- Dependencies MUST be injected via constructors — NEVER use global variables or
init()for service setup - Small projects (< 10 services) SHOULD use manual constructor injection — no library needed
- Interfaces MUST be defined where consumed, not where implemented — accept interfaces, return structs
- NEVER use global registries or package-level service locators
- The DI container MUST only exist at the composition root (
main()or app startup) — NEVER pass the container as a dependency - Prefer lazy initialization — only create services when first requested
- Use singletons for stateful services (DB connections, caches) and transients for stateless ones
- Mock at the interface boundary — DI makes this trivial
- Keep the dependency graph shallow — deep chains signal design problems
- Choose the right DI library for your project size and team — see the decision table below
Why Dependency Injection?
| Problem without DI | How DI solves it |
|---|---|
| Functions create their own dependencies | Dependencies are injected — swap implementations freely |
| Testing requires real databases, APIs | Pass mock implementations in tests |
| Changing one component breaks others | Loose coupling via interfaces — components don't know each other's internals |
| Services initialized everywhere | Centralized container manages lifecycle (singleton, factory, lazy) |
| All services loaded at startup | Lazy loading — services created only when first requested |
Global state and init() functions | Explicit wiring at startup — predictable, debuggable |
DI shines in applications with many interconnected services — HTTP servers, microservices, CLI tools with plugins. For a small script with 2-3 functions, manual wiring is fine. Don't over-engineer.
Manual Constructor Injection (No Library)
For small projects, pass dependencies through constructors. See Manual DI examples for a complete application example.
// ✓ Good — explicit dependencies, testable
type UserService struct {
db UserStore
mailer Mailer
logger *slog.Logger
}
func NewUserService(db UserStore, mailer Mailer, logger *slog.Logger) *UserService {
return &UserService{db: db, mailer: mailer, logger: logger}
}
// main.go — manual wiring
func main() {
logger := slog.Default()
db := postgres.NewUserStore(connStr)
mailer := smtp.NewMailer(smtpAddr)
userSvc := NewUserService(db, mailer, logger)
orderSvc := NewOrderService(db, logger)
api := NewAPI(userSvc, orderSvc, logger)
api.ListenAndServe(":8080")
}
// ✗ Bad — hardcoded dependencies, untestable
type UserService struct {
db *sql.DB
}
func NewUserService() *UserService {
db, _ := sql.Open("postgres", os.Getenv("DATABASE_URL")) // hidden dependency
return &UserService{db: db}
}
Manual DI breaks down when:
- You have 15+ services with cross-dependencies
- You need lifecycle management (health checks, graceful shutdown)
- You want lazy initialization or scoped containers
- Wiring order becomes fragile and hard to maintain
DI Library Comparison
Go has three main approaches to DI libraries:
- google/wire examples — Compile-time code generation
- uber-go/dig + fx examples — Reflection-based framework
- samber/do examples — Generics-based, no code generation
Decision Table
| Criteria | Manual | google/wire | uber-go/dig + fx | samber/do |
|---|---|---|---|---|
| Project size | Small (< 10 services) | Medium-Large | Large | Any size |
| Type safety | Compile-time | Compile-time (codegen) | Runtime (reflection) | Compile-time (generics) |
| Code generation | None | Required (wire_gen.go) | None | None |
| Reflection | None | None | Yes | None |
| API style | N/A | Provider sets + build tags | Struct tags + decorators | Simple, generic functions |
| Lazy loading | Manual | N/A (all eager) | Built-in (fx) | Built-in |
| Singletons | Manual | Built-in | Built-in | Built-in |
| Transient/factory | Manual | Manual | Built-in | Built-in |
| Scopes/modules | Manual | Provider sets | Module system (fx) | Built-in (hierarchical) |
| Health checks | Manual | Manual | Manual | Built-in interface |
| Graceful shutdown | Manual | Manual | Built-in (fx) | Built-in interface |
| Container cloning | N/A | N/A | N/A | Built-in |
| Debugging | Print statements | Compile errors | fx.Visualize() | ExplainInjector(), web interface |
| Go version | Any | Any | Any | 1.18+ (generics) |
| Learning curve | None | Medium | High | Low |
Quick Comparison: Wiring Style
The same graph — Config -> Database -> UserStore -> UserService -> API — wired by hand and by a container. The contrast is what the wiring code encodes: an ordered call sequence you maintain, versus a set of providers the container orders for you.
// Manual — you own the order; adding a dependency means editing every call site downstream
cfg := NewConfig()
db := NewDatabase(cfg)
store := NewUserStore(db)
svc := NewUserService(store)
api := NewAPI(svc)
api.Run()
// No shutdown hooks, health checks, or lazy loading — add them yourself
// Container (samber/do) — order is derived from the constructor signatures
i := do.New()
do.Provide(i, NewConfig)
do.Provide(i, NewDatabase)
do.Provide(i, NewUserStore)
do.Provide(i, NewUserService)
api := do.MustInvoke[*API](i)
api.Run()
defer i.Shutdown() // shutdown and health checks come from the container
google/wire and uber-go/fx express the same graph differently: wire generates the manual sequence above at build time from a wire.Build provider list (cleanup via func() returned by providers, no lifecycle hooks), while fx registers providers with fx.Provide and resolves them by reflection at runtime with OnStart/OnStop hooks. Full wiring examples for each: google/wire, uber-go/dig + fx, samber/do.
Testing with DI
DI makes testing straightforward — inject mocks instead of real implementations:
// Define a mock
type MockUserStore struct {
users map[string]*User
}
func (m *MockUserStore) FindByID(ctx context.Context, id string) (*User, error) {
u, ok := m.users[id]
if !ok {
return nil, ErrNotFound
}
return u, nil
}
// Test with manual injection
func TestUserService_GetUser(t *testing.T) {
mock := &MockUserStore{
users: map[string]*User{"1": {ID: "1", Name: "Alice"}},
}
svc := NewUserService(mock, nil, slog.Default())
user, err := svc.GetUser(context.Background(), "1")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if user.Name != "Alice" {
t.Errorf("got %q, want %q", user.Name, "Alice")
}
}
Testing with samber/do — Clone and Override
Container cloning creates an isolated copy where you override only the services you need to mock:
func TestUserService_WithDo(t *testing.T) {
// Create a test injector with mock implementation
testInjector := do.New()
// Provide the mock UserStore interface
do.OverrideValue[UserStore](testInjector, &MockUserStore{
users: map[string]*User{"1": {ID: "1", Name: "Alice"}},
})
// Provide other real services as needed
do.Provide[*slog.Logger](testInjector, func(i *do.Injector) (*slog.Logger, error) {
return slog.Default(), nil
})
svc := do.MustInvoke[*UserService](testInjector)
user, err := svc.GetUser(context.Background(), "1")
// ... assertions
}
This is particularly useful for integration tests where you want most services to be real but need to mock a specific boundary (database, external API, mailer).
When to Adopt a DI Library
| Signal | Action |
|---|---|
| < 10 services, simple dependencies | Stay with manual constructor injection |
| 10-20 services, some cross-cutting concerns | Consider a DI library |
| 20+ services, lifecycle management needed | Strongly recommended |
| Need health checks, graceful shutdown | Use a library with built-in lifecycle support |
| Team unfamiliar with DI concepts | Start manual, migrate incrementally |
Common Mistakes
| Mistake | Fix |
|---|---|
| Global variables as dependencies | Pass through constructors or DI container |
init() for service setup | Explicit initialization in main() or container |
| Depending on concrete types | Accept interfaces at consumption boundaries |
| Passing the container everywhere (service locator) | Inject specific dependencies, not the container |
| Deep dependency chains (A->B->C->D->E) | Flatten — most services should depend on repositories and config directly |
| Creating a new container per request | One container per application; use scopes for request-level isolation |
Cross-References
- → See
samber/cc-skills-golang@golang-samber-doskill for detailed samber/do usage patterns - → See
samber/cc-skills-golang@golang-structs-interfacesskill for interface design and composition - → See
samber/cc-skills-golang@golang-testingskill for testing with dependency injection - → See
samber/cc-skills-golang@golang-project-layoutskill for DI initialization placement
References
FAQ
What does the golang-dependency-injection skill do?
Comprehensive guide for dependency injection (DI) in Golang. Covers why DI matters (testability, loose coupling, separation of concerns, lifecycle management), manual constructor injection, and DI library comparison (google/wire, uber-go/dig, uber-go/fx, samber/do). Use this skill when designing service architecture, setting up dependency injection, refactoring tightly coupled code, managing singletons or service factories, or when the user asks about inversion of control, service containers, or wiring dependencies in Go. For a specific DI library, → See samber/cc-skills-golang@golang-google-wire, samber/cc-skills-golang@golang-uber-dig, samber/cc-skills-golang@golang-uber-fx, or samber/cc-skills-golang@golang-samber-do skills.
How do I install golang-dependency-injection?
Run npx skills add https://github.com/samber/cc-skills-golang --skill golang-dependency-injection in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/golang-dependency-injection/ (personal) or .claude/skills/golang-dependency-injection/ (project).
Which AI agents can use golang-dependency-injection?
Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.
Is golang-dependency-injection safe to install?
Security partners have reviewed it: Gen Agent Trust Hub passed; Socket passed; Snyk passed; ZeroLeaks passed. As with any skill, read the SKILL.md and bundled scripts before installing.
Who maintains golang-dependency-injection?
It is published by samber in samber/cc-skills-golang. It has 39,630 recorded installs.
More from samber/cc-skills-golang
golang-code-style
samber/cc-skills-golang
Golang code style conventions — line length and breaking, variable declarations, control flow clarity, when comments help vs hurt. Use when writing or reviewing Go code, asking about style or clarity, or establishing project coding standards. Not for naming conventions (→ See samber/cc-skills-golang@golang-naming skill), linter configuration (→ See samber/cc-skills-golang@golang-lint skill), or doc comments (→ See samber/cc-skills-golang@golang-documentation skill).
golang-testing
samber/cc-skills-golang
Production-ready Golang tests — table-driven tests, testify suites and mocks, parallel tests, fuzzing, fixtures, goroutine leak detection with goleak, snapshot testing, code coverage, integration tests, idiomatic test naming. Use when writing or reviewing Go tests, choosing a testing approach, setting up Go test CI, or debugging flaky/slow tests. For testify-specific APIs see samber/cc-skills-golang@golang-stretchr-testify; for measurement methodology see samber/cc-skills-golang@golang-benchmark.
golang-error-handling
samber/cc-skills-golang
Idiomatic Golang error handling — creation, wrapping with %w, errors.Is/As, errors.Join, custom error types, sentinel errors, panic/recover, the single handling rule, structured logging with slog, HTTP request logging middleware, and samber/oops for production errors. Built to make logs usable at scale with log aggregation 3rd-party tools. Apply when creating, wrapping, inspecting, or logging errors in Go code. For samber/oops specifics → See samber/cc-skills-golang@golang-samber-oops skill; for slog handler ecosystem → See samber/cc-skills-golang@golang-samber-slog skill.
golang-security
samber/cc-skills-golang
Security best practices and vulnerability prevention for Golang — injection (SQL, command, XSS), cryptography, path traversal, SSRF and HTTP security headers, cookies, secrets management, memory safety, PII in logs, STRIDE/DREAD threat modeling, plus gosec SAST, race detection, and fuzz testing. Apply when writing, reviewing, or auditing Go code for security, or when touching crypto, file or network I/O, secrets, user input, or authentication. Not for non-exploitable defensive bugs such as nil panics or slice aliasing (→ See samber/cc-skills-golang@golang-safety skill), dependency vulnerability scanning with govulncheck (→ See samber/cc-skills-golang@golang-dependency-management skill), or wiring security scanners into CI pipelines (→ See samber/cc-skills-golang@golang-continuous-integration skill).
golang-design-patterns
samber/cc-skills-golang
Idiomatic Golang design patterns — functional options, constructor APIs, init() and global-state avoidance, enums, panic vs error decisions, resource management and lifecycle, graceful shutdown, timeouts and retries, streaming and iterators, and architecture styles (clean, hexagonal, DDD, flat). Apply when choosing between architectural patterns, implementing functional options, designing constructor APIs, setting up graceful shutdown, applying resilience patterns, or asking which idiomatic Go pattern fits a specific problem. Not for wiring a DI container or comparing DI libraries (→ See samber/cc-skills-golang@golang-dependency-injection skill), nor for error wrapping, errors.Is/As, or logging mechanics (→ See samber/cc-skills-golang@golang-error-handling skill).
golang-performance
samber/cc-skills-golang
Golang performance optimization patterns and methodology - if X bottleneck, then apply Y. Covers allocation reduction, CPU efficiency, memory layout, GC tuning, pooling, caching, and hot-path optimization. Use when profiling or benchmarks have identified a bottleneck and you need the right optimization pattern to fix it. Also use when performing performance code review to suggest improvements or benchmarks that could help identify quick performance gains. Not for measurement methodology (→ See samber/cc-skills-golang@golang-benchmark skill) or debugging workflow (→ See samber/cc-skills-golang@golang-troubleshooting skill).
Popular backend & apis skills
entra-app-registration
microsoft/azure-skills
Guides Microsoft Entra ID app registration, OAuth 2.0 authentication, and MSAL integration. USE FOR: create app registration, register Azure AD app, configure OAuth, set up authentication, add API permissions, generate service principal, MSAL example, console app auth, Entra ID setup, Azure AD authentication. DO NOT USE FOR: Key Vault secrets (use azure-keyvault-expiration-audit), general Azure resource security guidance.
google-agents-cli-adk-code
google/agents-cli
This skill should be used when the user wants to "write agent code", "build an agent with ADK", "add a tool", "create a callback", "define an agent", "use state management" — in a project that needs ADK (Agent Development Kit) API patterns and code examples. It provides a quick reference for agent types, tool definitions, orchestration patterns, callbacks, state management, the graph Workflow API, and reference recipes to study. Do NOT use for scaffolding (use google-agents-cli-scaffold) or deployment (use google-agents-cli-deploy).
embedded-captions
heygen-com/hyperframes
Add captions or subtitles to an existing single-subject talking-head video without editing the footage. Use for plain verbatim captions, cinematic captions embedded behind the subject, VFX captions, “炸/特效/酷炫字幕,” or a named identity from the 35-style catalog. Route by visual identity, not by backend engine. The quiet anchor rail is the default; embed every word only when the user explicitly wants a fully cinematic treatment. The workflow runs locally end to end, including transcription and subject matting; split multi-shot footage before applying it.
entra-agent-id
microsoft/azure-skills
Provision Microsoft Entra Agent Identity Blueprints, BlueprintPrincipals, and per-instance Agent Identities via Microsoft Graph, and configure OAuth 2.0 token exchange (fmipath, OBO, cross-tenant) including the Microsoft Entra SDK for AgentID sidecar. USE FOR: Agent Identity Blueprint, BlueprintPrincipal, agent OAuth, fmipath token exchange, agent OBO, Workload Identity Federation for agents, polyglot agent auth, Microsoft.Identity.Web.AgentIdentities. DO NOT USE FOR: standard Entra app registration (use entra-app-registration), Microsoft Foundry agent authoring (use microsoft-foundry).
neon
neondatabase/agent-skills
Overview of Neon, a complete set of cloud backend primitives around Lakebase Postgres: Auth, Object Storage, Functions, the AI Gateway, and Realtime. Start here to choose Neon for undecided login, files, APIs, and LLM calls, set up the CLI or MCP server, and follow the branch-first workflow. Use when building an app or backend on Neon, or when "Neon" or "Lakebase Postgres" is mentioned. Child skill neon-postgres wins for an existing DATABASEURL, SQL, schema, inspect, or search. Child skill neon-auth wins for login, users, sessions, identity routing, and Managed Better Auth setup. Child skill neon-realtime wins for live queries in reactive apps and agents. Also use for object storage, S3, buckets, serverless functions, function triggers, cron, AI gateway, LLM calls, realtime, logs, Loki, Grafana, observability, postgres, database, backend, Claimable Neon, neon.new, or a no-signup database.
design-an-interface
mattpocock/skills
Generate multiple radically different interface designs for a module using parallel sub-agents. Use when user wants to design an API, explore interface options, compare module shapes, or mentions "design it twice".