Uber Fx application wiring
Use the application's existing Fx composition and lifecycle conventions, including its process-level start, stop, and signal ownership.
Inspect before editing
Locate fx.New, modules, providers, invokes, annotations, names and groups, decorators, lifecycle hooks, event logging, start/stop entry points, and fxtest coverage. Confirm the selected version in go.mod before using newer options.
Construction and start are different phases
fx.New builds the application definition and executes registered invokes; constructors needed by those invokes can therefore run while the app is being created. Start runs lifecycle OnStart hooks, and Stop runs corresponding OnStop hooks. Do not put work in a constructor on the assumption that it begins only during Start.
Check app.Err() or the error returned by the application's existing construction path. Use Run, Start/Stop, or signal handling according to how the host program already owns its process.
Preserve the executable boundary
Determine whether Fx owns the process run loop or is embedded in another command, server, or test harness. Keep one owner for signals, startup timeout, shutdown timeout, and final error rendering. An embedded application should normally expose bounded Start and Stop behavior rather than starting a second signal loop.
Keep construction errors distinct from start-hook and stop-hook failures because they imply different cleanup and retry behavior.
Modules and graph boundaries
- Keep providers independently testable and avoid retaining the Fx container as a service locator.
- Use
fx.In,fx.Out, or annotations where they make names, groups, optional inputs, or interface exposure explicit. Verify annotation support in the selected version. - Treat names and groups as API. Value-group order is unspecified; assemble ordered chains explicitly.
- Use optional dependencies only when absence is a supported state, not to mask an incomplete graph.
- Keep module-local decorators and replacements scoped intentionally. Review their visibility before moving options across module boundaries.
- Avoid invokes whose only purpose is hidden initialization; make the owned behavior and lifecycle visible.
Read references/api-and-testing.md for concrete fx.Provide, fx.Invoke, annotations, lifecycle hooks, replacements, and fxtest patterns.
Lifecycle invariants
Register hooks before the application starts. Start hooks run in registration order, and stop hooks run in reverse order for hooks whose starts succeeded. Respect the supplied contexts and the application's configured time budgets.
An OnStart hook should return after the resource is ready enough for dependents rather than blocking for the lifetime of a server. If it starts background work, retain ownership, surface startup failures, and stop or join it from OnStop.
Make partial-start failure safe: resources started before a later hook fails must still be stoppable. Keep shutdown idempotent only when the underlying resource contract supports it.
Validation and testing
Use the selected Fx version's graph-validation facility when only wiring needs validation; understand that validation may not execute constructors. Use fxtest or the project's equivalent when constructor behavior and lifecycle hooks matter. Test start failure, partial startup, cancellation, reverse-order shutdown, replacements, groups, and event logging affected by the change.
Run formatting, compilation, and relevant tests. Construct and start each changed application root under bounded test contexts, then stop it and verify owned resources are released.