Twelve Go Best Practices Francesc Campoy Flores Gopher at Google @francesc http://campoy.cat/+ https://go.dev * 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 /doc/gopher/gopherbw.png 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 ( "fmt" "io" "log" "golang.org/x/net/websocket" ) 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 golang.org 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) { [[https://pkg.go.dev/code.google.com/p/go.talks/pkg/present#Author][Generated documentation]] [[/blog/godoc-documenting-go-code][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` package. 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 [[https://github.com/bradfitz/camlistore]] * 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 Resources - Go homepage [[/][go.dev]] - Go interactive tour [[/tour/][go.dev/tour]] Other talks - Lexical scanning with Go [[http://www.youtube.com/watch?v=HxaD_trXwRE][video]] - Concurrency is not parallelism [[http://vimeo.com/49718712][video]] - Go concurrency patterns [[http://www.youtube.com/watch?v=f6kdp27TYZs][video]] - Advanced Go concurrency patterns [[http://www.youtube.com/watch?v=QDDwwePbDtw][video]]