These are the instructions for writing clean, maintainable and efficient code in golang.
Naming conventions:
- Use camelCase for unexported identifiers
- Use PascalCase for exported identifiers
- Package names should be short, lowercase and descriptive. Good:
net/http,image/png,testframework, Bad:kafka_topic_manager,helper_util - For single method interfaces use -er suffix. Good:
Reader,Writer,Closer, Bad:ReadInterface,IRead,ReadIface
Common best practices:
- Do not have multiple nested if statements, if possible extract them to switch statement
- Try to avoid nested else if statement
- Always use
context.Contextfor IO operations
Project structure:
- Avoid creating generic packages like
utils,helpers,common - Use internal/ for internal packages which should not be exposed
- For large applications that require multiple main files use cmd/ folders. Example: cmd/api/main.go, cmd/cli/main.go
- For sub packages use pkg/ folder.
- Do not have very large files, try to split them into smaller files by domain
Error handling:
- Never skip error checking
- Never use panic unless user explicitly asked for it
- When wrapping error or adding context using fmt.Errorf do not expose implementation details
- Avoid nesting, always check errors first
Bad:
func (g *Gopher) WriteTo(w io.Writer) (size int64, err error) {
err = binary.Write(w, binary.LittleEndian, int32(len(g.Name)))
if err == nil {
size += 4
var n int
n, err = w.Write([]byte(g.Name))
size += int64(n)
if err == nil {
err = binary.Write(w, binary.LittleEndian, int64(g.AgeYears))
if err == nil {
size += 4
}
return
}
return
}
return
}
Good:
func (g *Gopher) WriteTo(w io.Writer) (size int64, err error) {
err = binary.Write(w, binary.LittleEndian, int32(len(g.Name)))
if err != nil {
return
}
size += 4
n, err := w.Write([]byte(g.Name))
size += int64(n)
if err != nil {
return
}
err = binary.Write(w, binary.LittleEndian, int64(g.AgeYears))
if err == nil {
size += 4
}
return
}
Concurrency best practices:
- Use WaitGroup if you want to wait for multiple goroutines to finish
- Check ctx.Done() to check if context is cancelled to avoid doing unnecessary work