Files
Gumble-Backend/docs/server-initialization.md
Zane Walker 7e940c83a7
Some checks failed
Build & Test / build-test (push) Has been cancelled
Build & Test / swagger-codegen-cli (push) Has been cancelled
CodeQL / Analyze (go) (push) Has been cancelled
(Feat): Initial Commit
2026-07-03 19:41:31 +05:30

5.2 KiB

Server Initialization

As our projects have grown, we have gradually added more and more dependent components. Consequently, fully initializing the top-level Server struct has become a rather complex task, especially considering the lack of reliable protection against accidental usage of an uninitialized Server instance or attached services.

To simplify our workflow, we made the decision to integrate the wire code generation tool into our project. This tool effectively resolves the dependency graph and ensures that the server components are always initialized in the correct order.

Wire is a code generation tool that automates connecting components using dependency injection.

https://pkg.go.dev/github.com/google/wire.

To accomplish this, we had to introduce certain changes, which may be potentially breaking. We acknowledge that the downstream projects will require some effort in adapting to these changes. Nevertheless, we strongly believe that the long-term benefits will justify the initial investment.

Please read the following instructions carefully.

1. Define providers

BEFORE: server components initialized within the methods defined on the Server struct. For example:

func (s *Server) InitPush() error {

AFTER: providers defined according to the wire guideline: https://github.com/google/wire/blob/main/docs/guide.md#defining-providers.

Providers are just ordinary functions getting the necessary dependencies as input parameters and returning an initialized component. For example:

func NewPush(cfg config.Server, db *sql.DB) (*push.Service, error) {

REQUIRED ACTION

Convert all func (s *Server) Init* methods into providers conforming to the wire guidelines.

If for any reason a provider function can't live in it's dedicated package, you can place it in providers.go.

Please refer to internal/api/wire.go to check currently used wire providers. They are listed as params to the wire.Build() function.

2. Define injectors

Injectors are declared in internal/api/wire.go. They instruct wire which providers should be used to satisfy the dependencies of a top level component.

REQUIRED ACTION

Add the providers defined in the previous step to the InitNewServer* functions in internal/api/wire.go. More injectors might be added if needed.

Some hints:

3. Generate

Functions declared in wire.go are just recepies for code generation and are excluded from build with the +build wireinject directive. To generate the code, wire command has to be invoked in the directory where wire.go resides.

Generated code can appears in wire_gen.go and looks like:

func InitNewServer(cfg config.Server) (*Server, error) {
	db, err := NewDB(cfg)
	if err != nil {
		return nil, err
	}
	service, err := NewPush(cfg, db)

Because wire.go is excluded from build, function signatures can be duplicated in wire_gen.go. On any wire.go change, wire_gen.go needs to be updated.

Wire generation has been added to Makefile step go-generate.

REQUIRED ACTION

Wire tool installation:

make init

Wire generation:

wire gen ./...

or via make:

make go-generate

On errors you can simply remove wire_gen.go and try again - sometimes it helps. Otherwire, see this section or get familiar with the wire documentation.

4. Integrate into existing code

After successful generation, func (s *Server) Init* methods are no longer needed. We can remove them and update each place where they have been used previously to use newly generated functions.

REQUIRED ACTION

Remove all func (s *Server) Init* methods and run

make go-build

At each place where the compilation fails do the following:

  • remove the usage of these methods.
  • replace NewServer(cfg) or any other previously used Server provider with the corresponding function generated by wire, for example InitNewServer.

Sometimes having a fully initialized server is not required (see scripts/internal/handlers/check.go). In such cases, components we want to use need a manual initialization - the providers have to be invoked in the right order and the resulting components have to be assigned to the Server.

When you're done, verify the sweet fruits of your labor with:

make all
make test