Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Twelve Go Best Practices

Twelve Go Best Practices

Best practices to write idiomatic and effective Go code.
golang

Francesc Campoy Flores

September 12, 2014
Tweet

More Decks by Francesc Campoy Flores

Other Decks in Programming

Transcript

  1. Best practices From Wikipedia: " A b e s t

    p r a c t i c e i s a m e t h o d o r t e c h n i q u e t h a t h a s c o n s i s t e n t l y s h o w n r e s u l t s s u p e r i o r t o t h o s e a c h i e v e d w i t h o t h e r m e a n s " Techniques to write Go code that is simple, readable, maintainable.
  2. Some code t y p e G o p h

    e r s t r u c t { N a m e s t r i n g A g e Y e a r s i n t } f u n c ( g * G o p h e r ) W r i t e T o ( w i o . W r i t e r ) ( s i z e i n t 6 4 , e r r e r r o r ) { e r r = b i n a r y . W r i t e ( w , b i n a r y . L i t t l e E n d i a n , i n t 3 2 ( l e n ( g . N a m e ) ) ) i f e r r = = n i l { s i z e + = 4 v a r n i n t n , e r r = w . W r i t e ( [ ] b y t e ( g . N a m e ) ) s i z e + = i n t 6 4 ( n ) i f e r r = = n i l { e r r = b i n a r y . W r i t e ( w , b i n a r y . L i t t l e E n d i a n , i n t 6 4 ( g . A g e Y e a r s ) ) i f e r r = = n i l { s i z e + = 4 } r e t u r n } r e t u r n } r e t u r n }
  3. Avoid nesting by handling errors first f u n c

    ( g * G o p h e r ) W r i t e T o ( w i o . W r i t e r ) ( s i z e i n t 6 4 , e r r e r r o r ) { e r r = b i n a r y . W r i t e ( w , b i n a r y . L i t t l e E n d i a n , i n t 3 2 ( l e n ( g . N a m e ) ) ) i f e r r ! = n i l { r e t u r n } s i z e + = 4 n , e r r : = w . W r i t e ( [ ] b y t e ( g . N a m e ) ) s i z e + = i n t 6 4 ( n ) i f e r r ! = n i l { r e t u r n } e r r = b i n a r y . W r i t e ( w , b i n a r y . L i t t l e E n d i a n , i n t 6 4 ( g . A g e Y e a r s ) ) i f e r r = = n i l { s i z e + = 4 } r e t u r n } Less nesting means less cognitive load on the reader
  4. Avoid repetition when possible Deploy one-off utility types for simpler

    code t y p e b i n W r i t e r s t r u c t { w i o . W r i t e r s i z e i n t 6 4 e r r e r r o r } / / W r i t e w r i t e s a v a l u e t o t h e p r o v i d e d w r i t e r i n l i t t l e e n d i a n f o r m . f u n c ( w * b i n W r i t e r ) W r i t e ( v i n t e r f a c e { } ) { i f w . e r r ! = n i l { r e t u r n } i f w . e r r = b i n a r y . W r i t e ( w . w , b i n a r y . L i t t l e E n d i a n , v ) ; w . e r r = = n i l { w . s i z e + = i n t 6 4 ( b i n a r y . S i z e ( v ) ) } }
  5. Avoid repetition when possible Using b i n W r

    i t e r f u n c ( g * G o p h e r ) W r i t e T o ( w i o . W r i t e r ) ( i n t 6 4 , e r r o r ) { b w : = & b i n W r i t e r { w : w } b w . W r i t e ( i n t 3 2 ( l e n ( g . N a m e ) ) ) b w . W r i t e ( [ ] b y t e ( g . N a m e ) ) b w . W r i t e ( i n t 6 4 ( g . A g e Y e a r s ) ) r e t u r n b w . s i z e , b w . e r r }
  6. Type switch to handle special cases f u n c

    ( w * b i n W r i t e r ) W r i t e ( v i n t e r f a c e { } ) { i f w . e r r ! = n i l { r e t u r n } s w i t c h v . ( t y p e ) { c a s e s t r i n g : s : = v . ( s t r i n g ) w . W r i t e ( i n t 3 2 ( l e n ( s ) ) ) w . W r i t e ( [ ] b y t e ( s ) ) c a s e i n t : i : = v . ( i n t ) w . W r i t e ( i n t 6 4 ( i ) ) d e f a u l t : i f w . e r r = b i n a r y . W r i t e ( w . w , b i n a r y . L i t t l e E n d i a n , v ) ; w . e r r = = n i l { w . s i z e + = i n t 6 4 ( b i n a r y . S i z e ( v ) ) } } } f u n c ( g * G o p h e r ) W r i t e T o ( w i o . W r i t e r ) ( i n t 6 4 , e r r o r ) { b w : = & b i n W r i t e r { w : w } b w . W r i t e ( g . N a m e ) b w . W r i t e ( g . A g e Y e a r s ) r e t u r n b w . s i z e , b w . e r r }
  7. Type switch with short variable declaration f u n c

    ( w * b i n W r i t e r ) W r i t e ( v i n t e r f a c e { } ) { i f w . e r r ! = n i l { r e t u r n } s w i t c h x : = v . ( t y p e ) { c a s e s t r i n g : w . W r i t e ( i n t 3 2 ( l e n ( x ) ) ) w . W r i t e ( [ ] b y t e ( x ) ) c a s e i n t : w . W r i t e ( i n t 6 4 ( x ) ) d e f a u l t : i f w . e r r = b i n a r y . W r i t e ( w . w , b i n a r y . L i t t l e E n d i a n , v ) ; w . e r r = = n i l { w . s i z e + = i n t 6 4 ( b i n a r y . S i z e ( v ) ) } } }
  8. Writing everything or nothing t y p e b i

    n W r i t e r s t r u c t { w i o . W r i t e r b u f b y t e s . B u f f e r e r r e r r o r } / / W r i t e w r i t e s a v a l u e t o t h e p r o v i d e d w r i t e r i n l i t t l e e n d i a n f o r m . f u n c ( w * b i n W r i t e r ) W r i t e ( v i n t e r f a c e { } ) { i f w . e r r ! = n i l { r e t u r n } s w i t c h x : = v . ( t y p e ) { c a s e s t r i n g : w . W r i t e ( i n t 3 2 ( l e n ( x ) ) ) w . W r i t e ( [ ] b y t e ( x ) ) c a s e i n t : w . W r i t e ( i n t 6 4 ( x ) ) d e f a u l t : w . e r r = b i n a r y . W r i t e ( & w . b u f , b i n a r y . L i t t l e E n d i a n , v ) } }
  9. Writing everything or nothing / / F l u s

    h w r i t e s a n y p e n d i n g v a l u e s i n t o t h e w r i t e r i f n o e r r o r h a s o c c u r r e d . / / I f a n e r r o r h a s o c c u r r e d , e a r l i e r o r w i t h a w r i t e b y F l u s h , t h e e r r o r i s / / r e t u r n e d . f u n c ( w * b i n W r i t e r ) F l u s h ( ) ( i n t 6 4 , e r r o r ) { i f w . e r r ! = n i l { r e t u r n 0 , w . e r r } r e t u r n w . b u f . W r i t e T o ( w . w ) } f u n c ( g * G o p h e r ) W r i t e T o ( w i o . W r i t e r ) ( i n t 6 4 , e r r o r ) { b w : = & b i n W r i t e r { w : w } b w . W r i t e ( g . N a m e ) b w . W r i t e ( g . A g e Y e a r s ) r e t u r n b w . F l u s h ( ) }
  10. Function adapters f u n c i n i t

    ( ) { h t t p . H a n d l e F u n c ( " / " , h a n d l e r ) } f u n c h a n d l e r ( w h t t p . R e s p o n s e W r i t e r , r * h t t p . R e q u e s t ) { e r r : = d o T h i s ( ) i f e r r ! = n i l { h t t p . E r r o r ( w , e r r . E r r o r ( ) , h t t p . S t a t u s I n t e r n a l S e r v e r E r r o r ) l o g . P r i n t f ( " h a n d l i n g % q : % v " , r . R e q u e s t U R I , e r r ) r e t u r n } e r r = d o T h a t ( ) i f e r r ! = n i l { h t t p . E r r o r ( w , e r r . E r r o r ( ) , h t t p . S t a t u s I n t e r n a l S e r v e r E r r o r ) l o g . P r i n t f ( " h a n d l i n g % q : % v " , r . R e q u e s t U R I , e r r ) r e t u r n } }
  11. Function adapters f u n c i n i t

    ( ) { h t t p . H a n d l e F u n c ( " / " , e r r o r H a n d l e r ( b e t t e r H a n d l e r ) ) } f u n c e r r o r H a n d l e r ( f f u n c ( h t t p . R e s p o n s e W r i t e r , * h t t p . R e q u e s t ) e r r o r ) h t t p . H a n d l e r F u n c { r e t u r n f u n c ( w h t t p . R e s p o n s e W r i t e r , r * h t t p . R e q u e s t ) { e r r : = f ( w , r ) i f e r r ! = n i l { h t t p . E r r o r ( w , e r r . E r r o r ( ) , h t t p . S t a t u s I n t e r n a l S e r v e r E r r o r ) l o g . P r i n t f ( " h a n d l i n g % q : % v " , r . R e q u e s t U R I , e r r ) } } } f u n c b e t t e r H a n d l e r ( w h t t p . R e s p o n s e W r i t e r , r * h t t p . R e q u e s t ) e r r o r { i f e r r : = d o T h i s ( ) ; e r r ! = n i l { r e t u r n f m t . E r r o r f ( " d o i n g t h i s : % v " , e r r ) } i f e r r : = d o T h a t ( ) ; e r r ! = n i l { r e t u r n f m t . E r r o r f ( " d o i n g t h a t : % v " , e r r ) } r e t u r n n i l }
  12. Important code goes first License information, build tags, package documentation.

    Import statements, related groups separated by blank lines. i m p o r t ( " f m t " " i o " " l o g " " c o d e . g o o g l e . c o m / p / g o . n e t / w e b s o c k e t " ) The rest of the code starting with the most significant types, and ending with helper function and types.
  13. Document your code Package name, with the associated documentation before.

    / / P a c k a g e p l a y g r o u n d r e g i s t e r s a n H T T P h a n d l e r a t " / c o m p i l e " t h a t / / p r o x i e s r e q u e s t s t o t h e g o l a n g . o r g p l a y g r o u n d s e r v i c e . p a c k a g e p l a y g r o u n d Exported identifiers appear in g o d o c , they should be documented correctly. / / A u t h o r r e p r e s e n t s t h e p e r s o n w h o w r o t e a n d / o r i s p r e s e n t i n g t h e d o c u m e n t . t y p e A u t h o r s t r u c t { E l e m [ ] E l e m } / / T e x t E l e m r e t u r n s t h e f i r s t t e x t e l e m e n t s o f t h e a u t h o r d e t a i l s . / / T h i s i s u s e d t o d i s p l a y t h e a u t h o r ' n a m e , j o b t i t l e , a n d c o m p a n y / / w i t h o u t t h e c o n t a c t d e t a i l s . f u n c ( p * A u t h o r ) T e x t E l e m ( ) ( e l e m s [ ] E l e m ) { Generated documentation (http://godoc.org/code.google.com/p/go.talks/pkg/present#Author) Gocode: documenting Go code (http://blog.golang.org/godoc-documenting-go-code)
  14. Shorter is better or at least longer is not always

    better. Try to find the shortest name that is self explanatory. Prefer M a r s h a l I n d e n t to M a r s h a l W i t h I n d e n t a t i o n . Don't forget that the package name will appear before the identifier you chose. In package e n c o d i n g / j s o n we find the type E n c o d e r , not J S O N E n c o d e r . It is referred as j s o n . E n c o d e r .
  15. Packages with multiple files Should you split a package into

    multiple files? Avoid very long files The n e t / h t t p package from the standard library contains 15734 lines in 47 files. Separate code and tests n e t / h t t p / c o o k i e . g o and n e t / h t t p / c o o k i e _ t e s t . g o are both part of the h t t p 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 d o c . g o containing the package documentation.
  16. 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. github.com/bradfitz/camlistore (https://github.com/bradfitz/camlistore)
  17. Ask for what you need Let's use the Gopher type

    from before t y p e G o p h e r s t r u c t { N a m e s t r i n g A g e Y e a r s i n t } We could define this method f u n c ( g * G o p h e r ) W r i t e T o F i l e ( f * o s . F i l e ) ( i n t 6 4 , e r r o r ) { But using a concrete type makes this code difficult to test, so we use an interface. f u n c ( g * G o p h e r ) W r i t e T o R e a d W r i t e r ( r w i o . R e a d W r i t e r ) ( i n t 6 4 , e r r o r ) { And, since we're using an interface, we should ask only for the methods we need. f u n c ( g * G o p h e r ) W r i t e T o W r i t e r ( f i o . W r i t e r ) ( i n t 6 4 , e r r o r ) {
  18. Keep independent packages independent i m p o r t

    ( " c o d e . g o o g l e . c o m / p / g o . t a l k s / 2 0 1 3 / b e s t p r a c t i c e s / f u n c d r a w / d r a w e r " " c o d e . g o o g l e . c o m / p / g o . t a l k s / 2 0 1 3 / b e s t p r a c t i c e s / f u n c d r a w / p a r s e r " ) / / P a r s e t h e t e x t i n t o a n e x e c u t a b l e f u n c t i o n . f , e r r : = p a r s e r . P a r s e ( t e x t ) i f e r r ! = n i l { l o g . F a t a l f ( " p a r s e % q : % v " , t e x t , e r r ) } / / C r e a t e a n i m a g e p l o t t i n g t h e f u n c t i o n . m : = d r a w e r . D r a w ( f , * w i d t h , * h e i g h t , * x m i n , * x m a x ) / / E n c o d e t h e i m a g e i n t o t h e s t a n d a r d o u t p u t . e r r = p n g . E n c o d e ( o s . S t d o u t , m ) i f e r r ! = n i l { l o g . F a t a l f ( " e n c o d e i m a g e : % v " , e r r ) }
  19. Parsing t y p e P a r s e

    d F u n c s t r u c t { t e x t s t r i n g e v a l f u n c ( f l o a t 6 4 ) f l o a t 6 4 } f u n c P a r s e ( t e x t s t r i n g ) ( * P a r s e d F u n c , e r r o r ) { f , e r r : = p a r s e ( t e x t ) i f e r r ! = n i l { r e t u r n n i l , e r r } r e t u r n & P a r s e d F u n c { t e x t : t e x t , e v a l : f } , n i l } f u n c ( f * P a r s e d F u n c ) E v a l ( x f l o a t 6 4 ) f l o a t 6 4 { r e t u r n f . e v a l ( x ) } f u n c ( f * P a r s e d F u n c ) S t r i n g ( ) s t r i n g { r e t u r n f . t e x t }
  20. Drawing i m p o r t ( " i

    m a g e " " c o d e . g o o g l e . c o m / p / g o . t a l k s / 2 0 1 3 / b e s t p r a c t i c e s / f u n c d r a w / p a r s e r " ) / / D r a w d r a w s a n i m a g e s h o w i n g a r e n d e r i n g o f t h e p a s s e d P a r s e d F u n c . f u n c D r a w P a r s e d F u n c ( f p a r s e r . P a r s e d F u n c ) i m a g e . I m a g e { Avoid dependency by using an interface. i m p o r t " i m a g e " / / F u n c t i o n r e p r e s e n t a d r a w a b l e m a t h e m a t i c a l f u n c t i o n . t y p e F u n c t i o n i n t e r f a c e { E v a l ( f l o a t 6 4 ) f l o a t 6 4 } / / D r a w d r a w s a n i m a g e s h o w i n g a r e n d e r i n g o f t h e p a s s e d F u n c t i o n . f u n c D r a w ( f F u n c t i o n ) i m a g e . I m a g e {
  21. Testing Using an interface instead of a concrete type makes

    testing easier. p a c k a g e d r a w e r i m p o r t ( " m a t h " " t e s t i n g " ) t y p e T e s t F u n c f u n c ( f l o a t 6 4 ) f l o a t 6 4 f u n c ( f T e s t F u n c ) E v a l ( x f l o a t 6 4 ) f l o a t 6 4 { r e t u r n f ( x ) } v a r ( i d e n t = T e s t F u n c ( f u n c ( x f l o a t 6 4 ) f l o a t 6 4 { r e t u r n x } ) s i n = T e s t F u n c ( m a t h . S i n ) ) f u n c T e s t D r a w _ I d e n t ( t * t e s t i n g . T ) { m : = D r a w ( i d e n t ) / / V e r i f y o b t a i n e d i m a g e .
  22. Avoid concurrency in your API What if we want to

    use it sequentially? f u n c d o C o n c u r r e n t l y ( j o b s t r i n g , e r r c h a n e r r o r ) { g o f u n c ( ) { f m t . P r i n t l n ( " d o i n g j o b " , j o b ) t i m e . S l e e p ( 1 * t i m e . S e c o n d ) e r r < - e r r o r s . N e w ( " s o m e t h i n g w e n t w r o n g ! " ) } ( ) } f u n c m a i n ( ) { j o b s : = [ ] s t r i n g { " o n e " , " t w o " , " t h r e e " } e r r c : = m a k e ( c h a n e r r o r ) f o r _ , j o b : = r a n g e j o b s { d o C o n c u r r e n t l y ( j o b , e r r c ) } f o r _ = r a n g e j o b s { i f e r r : = < - e r r c ; e r r ! = n i l { f m t . P r i n t l n ( e r r ) } } } Run
  23. Avoid concurrency in your API Expose synchronous APIs, calling them

    concurrently is easy. f u n c d o ( j o b s t r i n g ) e r r o r { f m t . P r i n t l n ( " d o i n g j o b " , j o b ) t i m e . S l e e p ( 1 * t i m e . S e c o n d ) r e t u r n e r r o r s . N e w ( " s o m e t h i n g w e n t w r o n g ! " ) } f u n c m a i n ( ) { j o b s : = [ ] s t r i n g { " o n e " , " t w o " , " t h r e e " } e r r c : = m a k e ( c h a n e r r o r ) f o r _ , j o b : = r a n g e j o b s { g o f u n c ( j o b s t r i n g ) { e r r c < - d o ( j o b ) } ( j o b ) } f o r _ = r a n g e j o b s { i f e r r : = < - e r r c ; e r r ! = n i l { f m t . P r i n t l n ( e r r ) } } } Run
  24. Use goroutines to manage state Use a chan or a

    struct with a chan to communicate with a goroutine t y p e S e r v e r s t r u c t { q u i t c h a n b o o l } f u n c N e w S e r v e r ( ) * S e r v e r { s : = & S e r v e r { m a k e ( c h a n b o o l ) } g o s . r u n ( ) r e t u r n s } f u n c ( s * S e r v e r ) r u n ( ) { f o r { s e l e c t { c a s e < - s . q u i t : f m t . P r i n t l n ( " f i n i s h i n g t a s k " ) t i m e . S l e e p ( t i m e . S e c o n d ) f m t . P r i n t l n ( " t a s k d o n e " ) s . q u i t < - t r u e r e t u r n c a s e < - t i m e . A f t e r ( t i m e . S e c o n d ) : f m t . P r i n t l n ( " r u n n i n g t a s k " ) } } }
  25. Use goroutines to manage state (continued) f u n c

    ( s * S e r v e r ) S t o p ( ) { f m t . P r i n t l n ( " s e r v e r s t o p p i n g " ) s . q u i t < - t r u e < - s . q u i t f m t . P r i n t l n ( " s e r v e r s t o p p e d " ) } f u n c m a i n ( ) { s : = N e w S e r v e r ( ) t i m e . S l e e p ( 2 * t i m e . S e c o n d ) s . S t o p ( ) } Run
  26. Avoid goroutine leaks with buffered chans f u n c

    s e n d M s g ( m s g , a d d r s t r i n g ) e r r o r { c o n n , e r r : = n e t . D i a l ( " t c p " , a d d r ) i f e r r ! = n i l { r e t u r n e r r } d e f e r c o n n . C l o s e ( ) _ , e r r = f m t . F p r i n t ( c o n n , m s g ) r e t u r n e r r } f u n c m a i n ( ) { a d d r : = [ ] s t r i n g { " l o c a l h o s t : 8 0 8 0 " , " h t t p : / / g o o g l e . c o m " } e r r : = b r o a d c a s t M s g ( " h i " , a d d r ) t i m e . S l e e p ( t i m e . S e c o n d ) i f e r r ! = n i l { f m t . P r i n t l n ( e r r ) r e t u r n } f m t . P r i n t l n ( " e v e r y t h i n g w e n t f i n e " ) }
  27. Avoid goroutine leaks with buffered chans (continued) the goroutine is

    blocked on the chan write the goroutine holds a reference to the chan the chan will never be garbage collected f u n c b r o a d c a s t M s g ( m s g s t r i n g , a d d r s [ ] s t r i n g ) e r r o r { e r r c : = m a k e ( c h a n e r r o r ) f o r _ , a d d r : = r a n g e a d d r s { g o f u n c ( a d d r s t r i n g ) { e r r c < - s e n d M s g ( m s g , a d d r ) f m t . P r i n t l n ( " d o n e " ) } ( a d d r ) } f o r _ = r a n g e a d d r s { i f e r r : = < - e r r c ; e r r ! = n i l { r e t u r n e r r } } r e t u r n n i l } Run
  28. Avoid goroutines leaks with buffered chans (continued) what if we

    can't predict the capacity of the channel? f u n c b r o a d c a s t M s g ( m s g s t r i n g , a d d r s [ ] s t r i n g ) e r r o r { e r r c : = m a k e ( c h a n e r r o r , l e n ( a d d r s ) ) f o r _ , a d d r : = r a n g e a d d r s { g o f u n c ( a d d r s t r i n g ) { e r r c < - s e n d M s g ( m s g , a d d r ) f m t . P r i n t l n ( " d o n e " ) } ( a d d r ) } f o r _ = r a n g e a d d r s { i f e r r : = < - e r r c ; e r r ! = n i l { r e t u r n e r r } } r e t u r n n i l } Run
  29. Avoid goroutines leaks with quit chan f u n c

    b r o a d c a s t M s g ( m s g s t r i n g , a d d r s [ ] s t r i n g ) e r r o r { e r r c : = m a k e ( c h a n e r r o r ) q u i t : = m a k e ( c h a n s t r u c t { } ) d e f e r c l o s e ( q u i t ) f o r _ , a d d r : = r a n g e a d d r s { g o f u n c ( a d d r s t r i n g ) { s e l e c t { c a s e e r r c < - s e n d M s g ( m s g , a d d r ) : f m t . P r i n t l n ( " d o n e " ) c a s e < - q u i t : f m t . P r i n t l n ( " q u i t " ) } } ( a d d r ) } f o r _ = r a n g e a d d r s { i f e r r : = < - e r r c ; e r r ! = n i l { r e t u r n e r r } } r e t u r n n i l } Run
  30. 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
  31. Some links Resources Go homepage golang.org (http://golang.org) Go interactive tour

    tour.golang.org (http://tour.golang.org) Other talks Lexical scanning with Go video (http://www.youtube.com/watch?v=HxaD_trXwRE) Concurrency is not parallelism video (http://vimeo.com/49718712) Go concurrency patterns video (http://www.youtube.com/watch?v=f6kdp27TYZs) Advanced Go concurrency patterns video (http://www.youtube.com/watch?v=QDDwwePbDtw)
  32. Thank you Francesc Campoy Flores Gopher at Google @francesc (http://twitter.com/francesc)

    http://campoy.cat/+ (http://campoy.cat/+) http://golang.org (http://golang.org)