ProgrammingBackend Developer

How does the init function work in Go? How does initialization occur within packages and how to properly manage dependencies and the order of initialization?

Pass interviews with Hintsage AI assistant

Answer.

Go supports the init function for performing initialization code before the main() starts. When running a Go program, the compiler automatically calls all init functions declared in each package after initializing the package variables.

Background: the need for one-time preparation of the package state before its usage.

Issue: the order of init initialization often leads to implicit errors: it is difficult to control when exactly a specific init function will be triggered, especially with multiple dependencies between packages.

Solution:

  • Any function with the signature func init() is allowed in each package. The calls occur in the order determined by the import dependencies. It is recommended not to abuse init functions — they should contain minimal code.

Code example:

package example import "fmt" var Cfg string = "default" func init() { fmt.Println("example init") Cfg = "configured" }

Key features:

  • All init functions in a package are called after the initialization of global/package variables.
  • If there are multiple init functions, they are called in the same order as their declaration in the file.
  • The order of package initialization is determined by the import tree — dependencies first, then the package itself.

Trick questions.

Can you define more than one init function in a single file?

Yes, multiple init functions are allowed — they will be called in the order of their declaration.

What happens if a package is imported only indirectly (via _ "package")?

Only the init functions and the initialization of this package's variables will be executed.

Can init functions return a value or an error?

No. The signature of init is immutable: func init(), with no parameters and no return values.

Typical mistakes and anti-patterns

  • Using init for complex business logic or heavy initialization.
  • Hidden changes to variables through init in different packages, leading to unpredictable behavior.
  • Multiple init functions without necessity.

Real-life example

Negative case

A large project relied on complex init functions that initialized global states with dependencies between packages, causing floating runtime errors.

Pros:

  • Simplified initialization (no explicit call in main).

Cons:

  • Dependency on order and access complicated testing and debugging.

Positive case

Init is used only for basic or test-specific tasks, with everything else moved to explicit functions called from main(). The order of execution is clear, and testing is simple.

Pros:

  • Transparency of execution and management of dependencies.

Cons:

  • The necessity to explicitly call the initialization function, a bit more code.