Wiki

Production-ready Go

Logging

Abort through log.Fatal instead of os.Exit to allow logger to flush buffered logs.

Use structured JSON logging in production. I recommend uber-go/zap or rs/zerolog, both of which are performant with zero allocation.

Use Error, Don’t Panic, But Always Recover

A panic is delivered continuously up the stack until all functions in the current goroutine have returned, at which point the entire program, not just the goroutine, crashes.

We don’t want our server to crash and drop all requests and connections because of some rare bug for “index out of range”. And this bug may be from vendored code. As much as possible, we prefer failing the request instead of outages.

Hence, I recommend recover() even if you already use error everywhere.

1
2
3
4
5
6
7
8
9
func main() {
	defer func() {
		if r := recover(); r != nil {
			fmt.Println("Recover main.", r)
		}
	}()

	startServerThreads()
}

Use Strict yaml unmarshal

Strict yaml unmarshal can catch typos in the runtime config file. Fail fast is better than quiet undefined behaviors.

1
2
3
4
5
6
7
8
9
10
import "gopkg.in/yaml.v3"

dec := yaml.NewDecoder(f)
// KnownFields ensures that the keys in decoded mappings to
// exist as fields in the struct being decoded into.
dec.KnownFields(true)
var conf Config
if err := dec.Decode(&conf); err != nil {
	// ...
}