blob: b4dbb487245154820cd866a4216cea432a04d278 [file] [log] [blame]
Twelve Go Best Practices
Francesc Campoy Flores
Gopher at Google
* Best practices
From Wikipedia:
"A best practice is a method or technique that has consistently shown results superior
to those achieved with other means"
Techniques to write Go code that is
- simple,
- readable,
- maintainable.
.image 200 200
* Some code
.code bestpractices/shortercode1.go /type Gopher/,/^}/
.code bestpractices/shortercode1.go /WriteTo/,/^}/
* Avoid nesting by handling errors first
.code bestpractices/shortercode2.go /WriteTo/,/^}/
Less nesting means less cognitive load on the reader
* Avoid repetition when possible
Deploy one-off utility types for simpler code
.code bestpractices/shortercode3.go /binWriter/,/^}/
.code bestpractices/shortercode3.go /Write writes/,/^}/
* Avoid repetition when possible
Using `binWriter`
.code bestpractices/shortercode3.go /WriteTo/,/^}/
* Type switch to handle special cases
.code bestpractices/shortercode4.go /func .* Write/,/^}/
.code bestpractices/shortercode4.go /WriteTo/,/^}/
* Type switch with short variable declaration
.code bestpractices/shortercode5.go /func .* Write/,/^}/
* Writing everything or nothing
.code bestpractices/shortercode6.go /binWriter/,/^}/
.code bestpractices/shortercode6.go /Write writes/,/^}/
* Writing everything or nothing
.code bestpractices/shortercode6.go /Flush/,/^}/
.code bestpractices/shortercode6.go /func .* WriteTo/,/^}/
* Function adapters
.code bestpractices/httphandler.go /HANDLER1/,/HANDLER2/
* Function adapters
.code bestpractices/httphandler.go /HANDLER2/,/END/
* Organizing your code
* Important code goes first
License information, build tags, package documentation.
Import statements, related groups separated by blank lines.
import (
The rest of the code starting with the most significant types, and ending
with helper function and types.
* Document your code
Package name, with the associated documentation before.
// Package playground registers an HTTP handler at "/compile" that
// proxies requests to the playground service.
package playground
Exported identifiers appear in `godoc`, they should be documented correctly.
// Author represents the person who wrote and/or is presenting the document.
type Author struct {
Elem []Elem
// TextElem returns the first text elements of the author details.
// This is used to display the author' name, job title, and company
// without the contact details.
func (p *Author) TextElem() (elems []Elem) {
[[][Generated documentation]]
[[][Gocode: documenting Go code]]
* Shorter is better
or at least _longer_is_not_always_better_.
Try to find the *shortest*name*that*is*self*explanatory*.
- Prefer `MarshalIndent` to `MarshalWithIndentation`.
Don't forget that the package name will appear before the identifier you chose.
- In package `encoding/json` we find the type `Encoder`, not `JSONEncoder`.
- It is referred as `json.Encoder`.
* Packages with multiple files
Should you split a package into multiple files?
- Avoid very long files
The `net/http` package from the standard library contains 15734 lines in 47 files.
- Separate code and tests
`net/http/cookie.go` and `net/http/cookie_test.go` are both part of the `http`
Test code is compiled *only* at test time.
- Separated package documentation
When we have more than one file in a package, it's convention to create a `doc.go`
containing the package documentation.
* Make your packages "go get"-able
Some packages are potentially reusable, some others are not.
A package defining some network protocol might be reused while one defining
an executable command may not.
.image bestpractices/cmd.png
* APIs
* Ask for what you need
Let's use the Gopher type from before
.code bestpractices/shortercode1.go /type Gopher/,/^}/
We could define this method
.code bestpractices/shortercode1.go /WriteToFile/
But using a concrete type makes this code difficult to test, so we use an interface.
.code bestpractices/shortercode1.go /WriteToReadWriter/
And, since we're using an interface, we should ask only for the methods we need.
.code bestpractices/shortercode1.go /WriteToWriter/
* Keep independent packages independent
.code bestpractices/funcdraw/cmd/funcdraw.go /IMPORT/,/ENDIMPORT/
.code bestpractices/funcdraw/cmd/funcdraw.go /START/,/END/
* Parsing
.code bestpractices/funcdraw/parser/parser.go /START/,/END/
* Drawing
.code bestpractices/funcdraw/drawer/dependent.go /START/,/END/
Avoid dependency by using an interface.
.code bestpractices/funcdraw/drawer/drawer.go /START/,/END/
* Testing
Using an interface instead of a concrete type makes testing easier.
.code bestpractices/funcdraw/drawer/drawer_test.go ,/END/
* Avoid concurrency in your API
.play bestpractices/concurrency1.go /START/,/END/
What if we want to use it sequentially?
* Avoid concurrency in your API
.play bestpractices/concurrency2.go /START/,/END/
Expose synchronous APIs, calling them concurrently is easy.
* Best practices for concurrency
* Use goroutines to manage state
Use a chan or a struct with a chan to communicate with a goroutine
.code bestpractices/server.go /START/,/STOP/
* Use goroutines to manage state (continued)
.play bestpractices/server.go /STOP/,
* Avoid goroutine leaks with buffered chans
.code bestpractices/bufchan.go /SEND/,/BROADCAST/
.code bestpractices/bufchan.go /MAIN/,
* Avoid goroutine leaks with buffered chans (continued)
.play bestpractices/bufchan.go /BROADCAST/,/MAIN/
- the goroutine is blocked on the chan write
- the goroutine holds a reference to the chan
- the chan will never be garbage collected
* Avoid goroutines leaks with buffered chans (continued)
.play bestpractices/bufchanfix.go /BROADCAST/,/MAIN/
- what if we can't predict the capacity of the channel?
* Avoid goroutines leaks with quit chan
.play bestpractices/quitchan.go /BROADCAST/,/MAIN/
* Twelve best practices
1. Avoid nesting by handling errors first
2. Avoid repetition when possible
3. Important code goes first
4. Document your code
5. Shorter is better
6. Packages with multiple files
7. Make your packages "go get"-able
8. Ask for what you need
9. Keep independent packages independent
10. Avoid concurrency in your API
11. Use goroutines to manage state
12. Avoid goroutine leaks
* Some links
- Go homepage [[]]
- Go interactive tour [[]]
Other talks
- Lexical scanning with Go [[][video]]
- Concurrency is not parallelism [[][video]]
- Go concurrency patterns [[][video]]
- Advanced Go concurrency patterns [[][video]]