Wiki
Production-ready Go
Table of Contents
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 {
// ...
}