RepoPilotOpen in app →

uber-go/fx

A dependency injection based application framework for Go.

Healthy

Healthy across all four use cases

weakest axis
Use as dependencyHealthy

Permissive license, no critical CVEs, actively maintained — safe to depend on.

Fork & modifyHealthy

Has a license, tests, and CI — clean foundation to fork and modify.

Learn fromHealthy

Documented and popular — useful reference codebase to read through.

Deploy as-isHealthy

No critical CVEs, sane security posture — runnable as-is.

  • Last commit 4mo ago
  • 15 active contributors
  • Distributed ownership (top contributor 36% of recent commits)
Show all 7 evidence items →
  • MIT licensed
  • CI configured
  • Tests present
  • Slowing — last commit 4mo ago

Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests

Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.

Embed the "Healthy" badge

Paste into your README — live-updates from the latest cached analysis.

Variant:
RepoPilot: Healthy
[![RepoPilot: Healthy](https://repopilot.app/api/badge/uber-go/fx)](https://repopilot.app/r/uber-go/fx)

Paste at the top of your README.md — renders inline like a shields.io badge.

Preview social card (1200×630)

This card auto-renders when someone shares https://repopilot.app/r/uber-go/fx on X, Slack, or LinkedIn.

Onboarding doc

Onboarding: uber-go/fx

Generated by RepoPilot · 2026-05-09 · Source

🤖Agent protocol

If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:

  1. Verify the contract. Run the bash script in Verify before trusting below. If any check returns FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding.
  2. Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
  3. Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/uber-go/fx shows verifiable citations alongside every claim.

If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.

🎯Verdict

GO — Healthy across all four use cases

  • Last commit 4mo ago
  • 15 active contributors
  • Distributed ownership (top contributor 36% of recent commits)
  • MIT licensed
  • CI configured
  • Tests present
  • ⚠ Slowing — last commit 4mo ago

<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>

Verify before trusting

This artifact was generated by RepoPilot at a point in time. Before an agent acts on it, the checks below confirm that the live uber-go/fx repo on your machine still matches what RepoPilot saw. If any fail, the artifact is stale — regenerate it at repopilot.app/r/uber-go/fx.

What it runs against: a local clone of uber-go/fx — the script inspects git remote, the LICENSE file, file paths in the working tree, and git log. Read-only; no mutations.

| # | What we check | Why it matters | |---|---|---| | 1 | You're in uber-go/fx | Confirms the artifact applies here, not a fork | | 2 | License is still MIT | Catches relicense before you depend on it | | 3 | Default branch master exists | Catches branch renames | | 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code | | 5 | Last commit ≤ 161 days ago | Catches sudden abandonment since generation |

<details> <summary><b>Run all checks</b> — paste this script from inside your clone of <code>uber-go/fx</code></summary>
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of uber-go/fx. If you don't
# have one yet, run these first:
#
#   git clone https://github.com/uber-go/fx.git
#   cd fx
#
# Then paste this script. Every check is read-only — no mutations.

set +e
fail=0
ok()   { echo "ok:   $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }

# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
  echo "FAIL: not inside a git repository. cd into your clone of uber-go/fx and re-run."
  exit 2
fi

# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "uber-go/fx(\\.git)?\\b" \\
  && ok "origin remote is uber-go/fx" \\
  || miss "origin remote is not uber-go/fx (artifact may be from a fork)"

# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
   || grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
  && ok "license is MIT" \\
  || miss "license drift — was MIT at generation time"

# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
  && ok "default branch master exists" \\
  || miss "default branch master no longer exists"

# 4. Critical files exist
test -f "app.go" \\
  && ok "app.go" \\
  || miss "missing critical file: app.go"
test -f "doc.go" \\
  && ok "doc.go" \\
  || miss "missing critical file: doc.go"
test -f "decorate.go" \\
  && ok "decorate.go" \\
  || miss "missing critical file: decorate.go"
test -f "annotated.go" \\
  && ok "annotated.go" \\
  || miss "missing critical file: annotated.go"
test -f "broadcast.go" \\
  && ok "broadcast.go" \\
  || miss "missing critical file: broadcast.go"

# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 161 ]; then
  ok "last commit was $days_since_last days ago (artifact saw ~131d)"
else
  miss "last commit was $days_since_last days ago — artifact may be stale"
fi

echo
if [ "$fail" -eq 0 ]; then
  echo "artifact verified (0 failures) — safe to trust"
else
  echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/uber-go/fx"
  exit 1
fi

Each check prints ok: or FAIL:. The script exits non-zero if anything failed, so it composes cleanly into agent loops (./verify.sh || regenerate-and-retry).

</details>

TL;DR

Fx is a production-grade dependency injection framework for Go that eliminates the need for global state and init() functions by providing a declarative container system to manage service lifecycles and wiring. It's the backbone of Uber's internal Go services and solves the problem of building loosely-coupled, reusable components without manually managing construction order and dependency resolution. Single-package library structure: core DI logic in root directory (app.go, annotated.go, decorate.go) with no monorepo layout. Platform-specific implementations in separate files (app_windows.go, app_wasm.go, app_unixes.go). Comprehensive docs/ex/ directory contains progressive tutorials (get-started 01-05) and annotation examples. Tests colocated with implementation (*_test.go). Reliance on go.uber.org/dig package for the underlying graph resolver.

👥Who it's for

Go backend engineers at Uber and other organizations building microservices who need to eliminate global state, share components across teams, and manage complex dependency graphs without boilerplate. Framework maintainers and library authors who want to provide Fx-compatible packages.

🌱Maturity & risk

Highly mature and production-ready. Marked as v1 with strict SemVer guarantees, backed by Uber's internal usage across nearly all Go services, comprehensive test coverage (571k LOC with extensive test files like app_test.go, annotated_test.go), CI/CD via GitHub Actions (.github/workflows/go.yml), and active maintenance. This is battle-tested infrastructure code.

Low risk for production use. The codebase is stable (v1 SemVer lock), has minimal external dependencies (only go.uber.org/dig for graph resolution, go.uber.org/multierr, go.uber.org/zap), and strict no-breaking-changes policy until v2. Primary risk is understanding the DI paradigm deeply—incorrect wiring or circular dependencies can be subtle to debug. Maintenance is tied to Uber's commitment.

Active areas of work

Active maintenance with recent Go 1.24 support (evident in docs/go.mod), linting via .golangci.yml, and security scanning via FOSSA. The framework is stable—changes likely focus on minor enhancements, documentation improvements in docs/, and compatibility updates rather than breaking feature work. Examples in docs/ex/ suggest ongoing developer experience focus.

🚀Get running

git clone https://github.com/uber-go/fx.git
cd fx
go mod download
make test

Verify with go test ./... to run all tests.

Daily commands: This is a library, not a runnable application. To test: make test or go test ./.... To see examples: cd docs/ex/get-started/01-minimal && go test (each example has main_test.go). To build docs: cd docs && make (requires Python/Sphinx toolchain).

🗺️Map of the codebase

  • app.go — Core application struct and lifecycle management (Start, Stop, Run) — the entry point for all Fx applications
  • doc.go — Package documentation and public API surface definition — defines what Fx exports to users
  • decorate.go — Decorator pattern implementation for wrapping and intercepting dependencies in the container
  • annotated.go — Annotation system for marking dependencies with metadata (tags, names, optional flags) used during injection
  • broadcast.go — Hook broadcast mechanism for lifecycle events (OnStart, OnStop) — critical for coordinating component startup/shutdown
  • README.md — Project overview and core value propositions; required reading for understanding Fx's philosophy

🧩Components & responsibilities

  • fx.App (Go, context, os/signal) — Manages application lifecycle (Start, Stop, Run), holds the dependency container, orchestrates initialization and shutdown
    • Failure mode: Panics if cyclic dependencies detected; returns error if constructor fails
  • Container (dig wrapper) (Reflection, uber-go/dig) — Resolves dependencies from constructor functions, validates the graph, handles type matching and injection
    • Failure mode: Returns error if types cannot be matched or if circular dependencies exist
  • Annotation System (annotated.go) (Go structs, closures) — Wraps constructors with metadata (Name, Group, Optional) for fine-grained dependency disambiguation
    • Failure mode: Type checking at graph-build time; fails early if annotations conflict
  • Decorator (decorate.go) (Higher-order functions, reflection) — Allows wrapping existing provided types with middleware/cross-cutting concerns after graph construction
    • Failure mode: Runtime panic if decorator signature does not match target type
  • Hook Broadcast (broadcast.go) (Channels, goroutines, context) — Manages lifecycle hook registration and execution (OnStart, OnStop) in topological order
    • Failure mode: Stops shutdown if an OnStop hook times out; logs errors and continues

🔀Data flow

  • User code (fx.New)fx.App — User provides fx.Option builders (fx.Provide, fx.Invoke, fx.Decorate) to configure the app
  • fx.Appundefined — undefined

🛠️How to make changes

Add a new module with provided dependencies

  1. Create a new file (e.g., mymodule.go) with a Module function that returns fx.Option (docs/ex/modules/module.go)
  2. Define constructor functions for your types and use fx.Provide to register them (annotated.go)
  3. Use fx.Annotate to add metadata tags if your dependencies need disambiguation (annotated.go)
  4. Pass the module to fx.New(app.Module, fx.Invoke(...)) in your app initialization (app.go)

Add lifecycle hooks (startup/shutdown handlers)

  1. Define a struct with OnStart and/or OnStop methods matching the lifecycle hook signature (broadcast.go)
  2. Register the struct using fx.Provide or include it in a Module (app.go)
  3. Fx will automatically call hooks in dependency order during app.Run() startup and shutdown (app.go)

Create a named or optional dependency with annotation

  1. Use fx.Annotate to wrap your constructor with Name() or Optional() metadata (annotated.go)
  2. Consumers of that dependency must also use fx.Annotate with matching Name() to inject it (annotated_test.go)
  3. Optional dependencies allow injection to fail gracefully if not provided (annotated.go)

Decorate an existing dependency with middleware

  1. Use fx.Decorate to wrap an existing provided type with a decorator function (decorate.go)
  2. Decorator receives the original instance and returns a wrapped version (decorate_test.go)
  3. Pass fx.Decorate to fx.New alongside other options (app.go)

🔧Why these technologies

  • Go interfaces & reflection — Enables dynamic dependency discovery and type-safe injection without runtime configuration files
  • uber-go/dig (internal dependency) — Provides the underlying container graph resolution engine; Fx wraps it with lifecycle and annotation features
  • Context-based lifecycle — Allows graceful shutdown signaling and timeout control via Go's standard context package
  • Functional options pattern (fx.Option) — Allows composable, readable app configuration without globals or builders; enables optional features

⚖️Trade-offs already made

  • Reflection-based dependency resolution vs. code generation

    • Why: Reflection allows elegant, minimal boilerplate configuration but introduces runtime overhead
    • Consequence: Slightly slower startup (~100ms for typical apps) but vastly simpler developer experience than generated DI
  • Single-threaded dependency construction (ordered by graph)

    • Why: Ensures deterministic initialization and avoids race conditions in constructor logic
    • Consequence: Cannot parallelize independent constructors; acceptable for typical application startup
  • Singletons by default (all provided types are app-scoped)

    • Why: Simplifies mental model and prevents accidental resource leaks; matches Uber's production use case
    • Consequence: No built-in request-scoped or factory patterns; users must implement manually if needed
  • Explicit fx.Invoke for side-effects vs. automatic invocation

    • Why: Requires developers to opt-in to dependency usage, making the dependency graph explicit and testable
    • Consequence: Slightly more verbose; prevents unused dependency injection and accidental initialization

🚫Non-goals (don't propose these)

  • Does not provide request-scoped or prototype (non-singleton) dependency lifetimes
  • Does not support runtime or configuration-file-driven dependency registration
  • Does not offer async/awaitable constructor patterns (all constructors are synchronous)
  • Not a service mesh or runtime environment manager; only handles in-process DI
  • Does not provide built-in RPC or serialization for inter-service communication

🪤Traps & gotchas

Signal handling platform-specific: app.go behavior differs on Windows vs Unix—Windows has no SIGTERM support. Dig resolver complexity: Circular dependencies fail silently until graph execution—test your wiring early. Lifecycle ordering subtle: OnStart hooks run in registration order, OnStop runs in reverse—order matters for shutdown. Annotation casting fragile: annotated.go In/Out types require exact type matching; implicit conversions fail without clear errors. No nil checks on providers: Returning nil from a constructor doesn't error until the dependent tries to use it. Testing isolation: No built-in test container isolation—use NewApp per test or global cleanup can leak state.

🏗️Architecture

💡Concepts to learn

  • Directed Acyclic Graph (DAG) Dependency Resolution — Fx uses DAG construction to resolve dependencies; understanding topological sorting and cycle detection is critical for debugging wiring issues and understanding lifecycle guarantees
  • Constructor Injection — Fx's core pattern—dependencies are passed as function parameters rather than retrieved from a global container; enables testability and compile-time safety
  • Struct Tag Reflection-Based Annotation — Fx uses Go struct tags (e.g., fx:"a" or 'fx.In') and reflection in annotated.go to support optional dependencies and selective field injection without boilerplate
  • Lifecycle Hooks (OnStart/OnStop) — Fx manages resource initialization and cleanup via hook chaining; critical for graceful shutdown, database connection pools, and goroutine lifecycle management
  • Decorator Pattern (Middleware Wrapping) — Fx's Decorate() API allows wrapping existing constructors post-hoc for cross-cutting concerns (logging, metrics, caching) without modifying original code
  • Module Composition — Fx Modules enable packaging reusable components as black boxes with explicit provides/invokes contracts; essential for building shareable libraries within organizations
  • Signal Handling Abstraction (Platform-Specific) — Fx abstracts Unix signals (SIGTERM) vs Windows events (CTRL_C_EVENT) via app_windows.go/app_unixes.go; understanding this prevents shutdown hangs across platforms
  • uber-go/dig — Lower-level dependency graph resolver that Fx uses internally; essential for understanding how graph construction works
  • google/wire — Alternative compile-time DI framework for Go; solves similar problem but via code generation rather than runtime reflection
  • uber-go/zap — Structured logging library commonly used in Fx applications; examples in docs use zap for instrumentation
  • uber-go/multierr — Error aggregation library used by Fx for managing multiple lifecycle hook failures; essential dependency
  • grpc/grpc-go — Common Go gRPC framework that integrates cleanly with Fx for service lifecycle management in microservices

🪄PR ideas

To work on one of these in Claude Code or Cursor, paste: Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.

Add comprehensive tests for app_wasm.go and app_windows.go platform-specific implementations

The repo has platform-specific app implementations (app_wasm.go, app_windows.go, app_unixes.go) but only app_windows_test.go exists. The WASM and Unix implementations lack dedicated test files, creating blind spots for platform-specific behavior. This is critical for a DI framework used across different platforms.

  • [ ] Create app_wasm_test.go with tests for WASM-specific signal handling and lifecycle differences
  • [ ] Create app_unixes_test.go with Unix/Linux signal handling tests (SIGTERM, SIGINT, etc.)
  • [ ] Add tests verifying graceful shutdown behavior differs appropriately across platforms
  • [ ] Test platform-specific error conditions in app_internal_test.go or new platform test files

Add documentation and examples for the Decorate API (decorate.go)

The codebase has decorate.go and decorate_test.go (indicating the feature exists) but there's no corresponding example in docs/ex/ directory. Given that annotate has its own docs/ex/annotate/ folder with multiple examples, the Decorate feature deserves similar documentation. This is a discoverable gap for new users.

  • [ ] Create docs/ex/decorate/ directory with decorate examples
  • [ ] Add a basic example (docs/ex/decorate/basic.go and basic_test.go) showing simple decoration
  • [ ] Add an advanced example showing practical use case (e.g., decorating with logging/tracing middleware)
  • [ ] Link decorate examples from main README.md similar to how annotate examples are referenced

Add GitHub Action workflow to test examples in docs/ex/ directory

The docs/ex/ folder contains 15+ example packages (get-started, annotate, modules, etc.) with _test.go files, but there's no CI workflow that runs these tests. Currently only .github/workflows/go.yml exists for the main package. If example tests fail, new contributors won't be caught until manual review.

  • [ ] Create .github/workflows/docs-examples.yml that runs 'go test ./docs/ex/...'
  • [ ] Configure it to run on pull requests and commits to main
  • [ ] Add step to verify all example packages have consistent go.mod versions matching main module
  • [ ] Include coverage reporting for example tests (optional but valuable for detecting stale examples)

🌿Good first issues

  • Add comprehensive examples for fx.Decorate() pattern in docs/ex/ (currently only basic annotation examples exist; decorator pattern is powerful but underdocumented for real-world use cases like middleware chains)
  • Extend test coverage for app_windows.go shutdown signal handling (currently app_windows_test.go exists but lacks edge cases for Windows-specific CTRL_C_EVENT signal semantics vs Unix SIGTERM)
  • Create a troubleshooting guide in docs/ with concrete error messages and fixes (fx graph errors like 'missing dependency' or 'circular import' are difficult to debug without a reference; add examples of common mistakes and their resolution)

Top contributors

Click to expand

📝Recent commits

Click to expand
  • d5da5b0 — docs: correct typos in doc.go (#1282) (danjovich)
  • 6fab1b2 — all: modernize (#1276) (abhinav)
  • 21e34ec — lint: upgrade golangci-lint configuration (#1275) (abhinav)
  • b002d43 — ci: Test against Go 1.24 and 1.25 (#1274) (abhinav)
  • 0a30575 — Back to development. (#1273) (JacobOaks)
  • 443b55a — Prepare release v1.24.0 (#1272) (JacobOaks)
  • a086405 — Adding a before-call-begins fxevent, to help with troubleshooting (#1267) (Groxx)
  • 16999a7 — [chore] run CI on Go 1.24 (#1261) (sywhang)
  • f11bfed — feat: add a clearer error message during building annotation (#1090) (#1268) (90Byte)
  • 3b94b13 — fix: update an invalid check in golangci-lint govet config (#1269) (tchung1118)

🔒Security observations

The codebase demonstrates generally good security practices as a dependency injection framework. No hardcoded secrets, SQL injection risks, or XSS vulnerabilities were identified in the file structure. However, there are outdated dependencies (particularly golang.org/x/sys from April 2022) that should be updated to mitigate potential inherited vulnerabilities. The framework itself, being a pure Go DI library without external I/O operations, has limited attack surface. The main recommendation is to establish a regular dependency update schedule and monitor Go security advisories.

  • Medium · Outdated Dependency: golang.org/x/sys — docs/go.sum - golang.org/x/sys v0.0.0-20220412211240-33da011f77ad. The golang.org/x/sys dependency is pinned to version 0.0.0-20220412211240-33da011f77ad, which is from April 2022. This is significantly outdated and may contain known security vulnerabilities. The Go security advisory database should be checked for CVEs affecting this version. Fix: Update golang.org/x/sys to the latest stable version using 'go get -u golang.org/x/sys' and run 'go mod tidy'. Consider setting up dependabot or similar tools to automate security updates.
  • Low · Outdated Test Dependency: github.com/stretchr/testify — docs/go.sum - github.com/stretchr/testify v1.8.1. The testify dependency is pinned to v1.8.1 from 2022. While this is primarily a test dependency with lower risk, it should still be kept current for any potential security or stability improvements. Fix: Update to the latest version of testify. Run 'go get -u github.com/stretchr/testify@latest' in the docs directory.
  • Low · Local Dependency Override Without Version Lock — docs/go.mod - replace directive. The go.mod file contains 'replace go.uber.org/fx => ../' which redirects to the parent directory. While this is common for monorepo development, it bypasses version pinning and could introduce unexpected changes if the parent module is modified. Fix: Document this pattern clearly in CONTRIBUTING.md. Ensure CI/CD pipelines verify compatibility when the parent module changes. Consider using absolute paths or git references for production builds.

LLM-derived; treat as a starting point, not a security audit.


Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.

Healthy signals · uber-go/fx — RepoPilot