| Twelve Go Best Practices |
| |
| Francesc Campoy Flores |
| Gopher at Google |
| @francesc |
| http://campoy.cat/+ |
| |
| http://golang.org |
| |
| * 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 http://golang.org/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) { |
| |
| [[http://godoc.org/code.google.com/p/go.talks/pkg/present#Author][Generated documentation]] |
| |
| [[http://blog.golang.org/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 [[http://golang.org]] |
| - Go interactive tour [[http://tour.golang.org]] |
| |
| 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]] |