Golang init function: syntax, rules, and order of execution

Golang init and go init: func init() rules, init order across imports and files, multiple init functions, blank imports, when to use or avoid init(), with spec-aligned examples.

Published

Updated

Read time 5 min read

Reviewed byDeepak Prasad

Golang init function: syntax, rules, and order of execution

The Go init hook surprises many newcomers because you can write func init() without calling it anywhere, and the runtime still runs it. Searches such as init golang, golang init, golang init function, go init function, golang func init, go func init, init function golang, func init golang, init function in golang, and golang init() usually boil down to two questions—what init may do, and in what order it runs relative to variables, imports, and main. This article answers both and ends with practical guidance on when init is a good fit.

Tested with Go 1.24 on Linux.


What the golang init function is

In Go, init is not something you invoke by name. You declare one or more functions named init with no parameters and no results:

go
func init() {
	// runs automatically during package initialization
}

The compiler treats each func init() as part of package initialization: package-level variables are set up first, then every init in that package, before main runs in a package main program.

Useful rules:

  • init is optional; many packages never define it.
  • A package may define multiple init functions, including across several .go files.
  • You cannot call init from your own code, and you cannot refer to init like a normal function value.

Order of execution: packages, variables, and init

Understanding golang init order means separating startup into layers: imported packages first (dependency order), then your main package.

Imported packages

When main imports other packages, Go walks the import graph in a valid dependency order. If main imports a and a imports b, then b is initialized before a, and a before your main package runs its own initialization. If main imports both a and b and neither imports the other, the language still picks a valid order; do not rely on unspecified tie-breaking between independent packages for program correctness.

For each package, initialization runs in this order:

  1. Package-level variable initializers (respecting dependencies between variables).
  2. Every init function in that package, in the order they appear in the source, file by file, with files ordered lexically by file name.

So golang init function order follows imports and the spec’s initialization algorithm, not “alphabetical package names” as a primary rule.

The main package

After every imported package needed by main has finished variable setup and init work, the main package’s own package-level variables run, then its init functions, and finally main().

Single file: variables before init, init before main

In one file, variable initializers still run before init, and all init functions run before main:

go
package main

import "fmt"

var sequence = initSequence()

func initSequence() int {
	fmt.Println("initSequence()")
	return 0
}

func init() {
	fmt.Println("init 1")
}

func init() {
	fmt.Println("init 2")
}

func main() {
	fmt.Println("main")
}
Output
text
initSequence()
init 1
init 2
main

Multiple files in one package: file name order

If init functions live in different files of the same package, Go processes files in lexical order by name. The example below is meant to be split into three files in one directory; run with go run . from that folder.

go
// a.go
package main

import "fmt"

func init() {
	fmt.Println("init a.go")
}
go
// z.go
package main

import "fmt"

func init() {
	fmt.Println("init z.go")
}
go
// main.go
package main

import "fmt"

func main() {
	fmt.Println("main")
}

Because a.go sorts before z.go, you should see init a.go before init z.go, then main.


Multi-package example (go init across packages)

Here main imports two sibling packages. Each prints from init and exposes a small API. Every imported package finishes variables and init before the importer’s init and main run. Save the layout on disk (for example under a module root) and run go run . from that directory; the Playground-style Run button is not used here because multiple files and local module paths are required.

text
module example.com/initdemo

go 1.24
go
// a/a.go
package a

import "fmt"

func init() {
	fmt.Println("init a")
}

func Greet() {
	fmt.Println("greet a")
}
go
// b/b.go
package b

import "fmt"

func init() {
	fmt.Println("init b")
}

func Greet() {
	fmt.Println("greet b")
}
go
// main.go
package main

import (
	"fmt"

	"example.com/initdemo/a"
	"example.com/initdemo/b"
)

func init() {
	fmt.Println("init main")
}

func main() {
	fmt.Println("main")
	a.Greet()
	b.Greet()
}
text
init a
init b
init main
main
greet a
greet b

If you are creating the tree from scratch, run go mod init example.com/initdemo once at the module root, then go run ..


Blank imports and side-effect inits

A line such as import _ "some/driver" still runs initialization for some/driver: its package-level variables and init functions execute because the import is part of the program, even though you never reference the package name. That pattern is common for SQL drivers and image format registration.


When to use or avoid golang init()

init is powerful because it runs automatically, which is also why it is easy to overuse.

Reasonable uses:

  • One-time registration that must happen at process start (for example database/sql drivers or image format decoders) where the library’s API is built around Register in init.
  • Small, deterministic package-level setup that cannot fail, or where failing fast with a panic before main is an explicit product decision (still a last resort).

Prefer not to use init for:

  • Work that can fail (network, files, parsing config): return errors from main, constructors, or explicit start functions instead.
  • Global mutable state hidden behind init, which complicates tests and reuse.
  • Libraries that impose heavy side effects on every importer.

If you feel you need init order between two packages you control, treat that as a design smell: prefer explicit functions and wiring from main instead of hidden coupling through init.


Summary

The Go init function is any func init() in a package. After imported packages are initialized in dependency order, each package runs its variable initializers, then every init in that package—ordered by file name across files and by source order within a file—before your main package does the same and finally calls main. Use init for narrow registration and similar package-local hooks; keep failure-prone or policy-heavy setup in ordinary functions and main so startup stays obvious to readers and tests.


References


Frequently Asked Questions

1. What is the golang init function?

In Go you declare init with func init() { ... }; the toolchain runs every init in a package during package initialization, after package-level variables are ready and before main in a main package. A package may define several init functions across multiple .go files.

2. What is the order of execution for golang init?

Packages initialize in dependency order following the import graph. Within each package, variable initializers run first (respecting dependencies), then init functions in source order per file, with files taken in lexical order by file name. After imports are done, the main package runs its variables and init functions, then main().

3. Can you have multiple init functions in Go?

Yes. You can declare many func init() bodies in one package, including in different files; they all run during that package initialization step, ordered by file name then source order.

4. When should you use init() in Go?

Use init sparingly for one-time registration or other package-local setup that must run before other code in the package, such as sql driver or image format registration. Prefer explicit setup from main, constructors, or Start-style APIs when work can fail or needs tests without global side effects.

5. Does init run before main in Go?

Yes. Imported packages finish variable setup and init first; then your main package runs its variables and init functions, then main() is called.
Tuan Nguyen

Data Scientist

Proficient in Golang, Python, Java, MongoDB, Selenium, Spring Boot, Kubernetes, Scrapy, API development, Docker, Data Scraping, PrimeFaces, Linux, Data Structures, and Data Mining. With expertise …