Given at GopherFest 2015. This is an updated version of the talk I gave in NYC Nov 14 at GothamGo.
“We need to think about failure differently. Most people think mistakes are a necessary evil. Mistakes aren't a necessary evil, they aren't evil at all. They are an inevitable consequence of doing something new and as such should be seen as valuable. “ - Ed Catmull
As Go is a "new" programming language we are all experimenting and learning how to write better Go. While most presentations focus on the destination, this presentation focuses on the journey of learning Go and the mistakes I personally made while developing Hugo, Cobra, Viper, Afero & Docker.
4. f
e
h
W U
w k
M
w
P n
I
v
ZA
S
U
G
k
C
w
P
U
“Do you want to know the
difference between a
master and a beginner?
The master has failed
more times than the
beginner has tried.”
5. f
e
h
W U
w k
M
w
P n
I
v
ZA
S
U
G
k
C
w
P
U
–Ed Catmull
“We need to think about failure
differently.
Most people think mistakes are a
necessary evil. Mistakes aren't a
necessary evil, they aren't evil at
all. They are an inevitable
consequence of doing something
new and as such should be seen
as valuable. “
6.
7.
8. f
e
h
W U
w k
M
w
P n
I
v
ZA
S
U
G
k
C
w
P
U
Mistake .
Not Accepting
Interfaces
1
9. f
e
h
W U
w k
M
w
P n
I
v
ZA
S
U
G
k
C
w
P
U
State & Behavior
•Types can express state &
behavior
•State = data structure
•Behavior = methods
10. f
e
h
W U
w k
M
w
P n
I
v
ZA
S
U
G
k
C
w
P
U
Interfaces
•One of Go’s most powerful
features
•Permits extensibility
•Defined by methods
•Adherence is only satisfied by
behavior
11. f
e
h
W U
w k
M
w
P n
I
v
ZA
S
U
G
k
C
w
P
U
•Fastest static site
generator
•Native Go
•35+ themes
•Flexible
•100s of contributors
•Powers GopherAcademy
gohugo.io
16. f
e
h
W U
w k
M
w
P n
I
v
ZA
S
U
G
k
C
w
P
U
Mistake .
Not Using
Io.Reader &
Io.Writer
2
17. f
e
h
W U
w k
M
w
P n
I
v
ZA
S
U
G
k
C
w
P
U
Io.Reader & Io.Writer
•Simple & flexible interfaces
for many operations around
input and output
•Provides access to a huge
wealth of functionality
•Keeps operations extensible
19. f
e
h
W U
w k
M
w
P n
I
v
ZA
S
U
G
k
C
w
P
U
Cobra Cli
Commander
•Fast and flexible
•Powers
Kubernetes & Hugo
•Provides subcommands, help,
man pages, bash autocomplete
github.com/spf13/cobra
20. // SetOutput sets the destination for
usage and error messages.
// If output is nil, os.Stderr is used.
func (c *Command) SetOutput(o io.Writer) {
c.output = o
}
Cobra Apps Enabled
21. f
e
h
W U
w k
M
w
P n
I
v
ZA
S
U
G
k
C
w
P
U
Viper
•Configuration management
•Supports json, yaml, toml,
defaults, flags, env vars &
remote key value
•Supports nesting, cascading
& aliases
github.com/spf13/viper
24. f
e
h
W U
w k
M
w
P n
I
v
ZA
S
U
G
k
C
w
P
U
Mistake .
Requiring Broad
Interfaces
3
25. f
e
h
W U
w k
M
w
P n
I
v
ZA
S
U
G
k
C
w
P
U
Interfaces Are Composable
•Functions should only accept interfaces
that require the methods they need
•Functions should not accept a broad
interface when a narrow one would
work
•Compose broad interfaces made from
narrower ones
26. f
e
h
W U
w k
M
w
P n
I
v
ZA
S
U
G
k
C
w
P
U
Afero Fs
•File system abstraction
•Uses standard OS interfaces
•Drop in replacement for OS
•Great for testing & mocking
•Cross platform memory
backed filesystem
github.com/spf13/afero
28. func ReadIn(f File) {
b := []byte{}
n, err := f.Read(b)
...
}
Requiring Broad Interfaces
29. func ReadIn(r Reader) {
b := []byte{}
n, err := r.Read(b)
...
}
Requiring Narrow Interfaces
30. f
e
h
W U
w k
M
w
P n
I
v
ZA
S
U
G
k
C
w
P
U
Mistake .
Methods Vs
Functions
4
31. f
e
h
W U
w k
M
w
P n
I
v
ZA
S
U
G
k
C
w
P
U
Too Many Methods
•A lot of people from OO
backgrounds overuse
methods
•Natural draw to define
everything via structs and
methods
32. f
e
h
W U
w k
M
w
P n
I
v
ZA
S
U
G
k
C
w
P
U
What Is A Function?
•Operations performed on N1
inputs that results in N2 outputs
•The same inputs will always
result in the same outputs
•Functions should not depend on
state
33. f
e
h
W U
w k
M
w
P n
I
v
ZA
S
U
G
k
C
w
P
U
What Is A Method?
•Defines the behavior of a type
•A function that operates
against a value
•Should use state
•Logically connected
34. f
e
h
W U
w k
M
w
P n
I
v
ZA
S
U
G
k
C
w
P
U
Functions Can Be Used With
Interfaces
•Methods, by definition, are
bound to a specific type
•Functions can accept
interfaces as input
35.
36. func extractShortcodes(s string, p *Page, t
Template) (string, map[string]shortcode, error) {
...
for {
switch currItem.typ {
...
case tError:
err := fmt.Errorf("%s:%d: %s",
p.BaseFileName(),
(p.lineNumRawContentStart()
+ pt.lexer.lineNum() - 1), currItem)
}
}
...
}
Example From Hugo
37. func extractShortcodes(s string, p *Page, t
Template) (string, map[string]shortcode, error) {
...
for {
switch currItem.typ {
...
case tError:
err := fmt.Errorf("%s:%d: %s",
p.BaseFileName(),
(p.lineNumRawContentStart()
+ pt.lexer.lineNum() - 1), currItem)
}
}
...
}
Example From Hugo
39. f
e
h
W U
w k
M
w
P n
I
v
ZA
S
U
G
k
C
w
P
U
Pointers Vs Values
•It’s not a question of performance
(generally), but one of shared access
•If you want to share the value with
a function or method, then use a
pointer
•If you don’t want to share it, then
use a value (copy)
41. f
e
h
W U
w k
M
w
P n
I
v
ZA
S
U
G
k
C
w
P
U
Pointer Receivers
•If you want to share a value with
it’s method, use a pointer
receiver
•Since methods commonly manage
state, this is the common usage
•Not safe for concurrent access
42. f
e
h
W U
w k
M
w
P n
I
v
ZA
S
U
G
k
C
w
P
U
Value Receivers
•If you want the value copied
(not shared), use values
•If the type is an empty struct
(stateless, just behavior)…
then just use value
•Safe for concurrent access
43.
44. type InMemoryFile struct {
at int64
name string
data []byte
closed bool
}
func (f *InMemoryFile) Close() error {
atomic.StoreInt64(&f.at, 0)
f.closed = true
return nil
}
Afero File
45. type Time struct {
sec int64
nsec uintptr
loc *Location
}
func (t Time) IsZero() bool {
return t.sec == 0 && t.nsec == 0
}
Time
46. f
e
h
W U
w k
M
w
P n
I
v
ZA
S
U
G
k
C
w
P
U
Mistake .
Thinking Of
Errors As Strings
6
48. f
e
h
W U
w k
M
w
P n
I
v
ZA
S
U
G
k
C
w
P
U
Standard Errors
•errors.New(“error here”) is
usually sufficient
•Exported Error Variables
can be easily checked
53. f
e
h
W U
w k
M
w
P n
I
v
ZA
S
U
G
k
C
w
P
U
Custom Errors
•Can provide context to
guarantee consistent feedback
•Provide a type which can be
different from the error value
•Can provide dynamic values
(based on internal error state)
54. f
e
h
W U
w k
M
w
P n
I
v
ZA
S
U
G
k
C
w
P
U
•Container runtime
& image format
•Native Go
•22k stars
•1000+ contributors
docker.com
55. type Error struct {
Code ErrorCode
Message string
Detail interface{}
}
// Error returns a human readable representation of the error.
func (e Error) Error() string {
return fmt.Sprintf("%s: %s",
strings.ToLower(strings.Replace(e.Code.String(), "_", " ",
-1)), e.Message)
}
Internationalization Of Errors
56. f
e
h
W U
w k
M
w
P n
I
v
ZA
S
U
G
k
C
w
P
U
Go StdLib
•Standard Go
Libraries
•Comprehensive and
powerful
•Great examples of “good” Go
http://golang.org/pkg/
57. // Portable analogs of some common system call errors.
var ErrInvalid = errors.New("invalid argument")
var ErrPermission = errors.New("permission denied")
// PathError records an error and
// the operation and file path that caused it.
type PathError struct {
Op string
Path string
Err error
}
func (e *PathError) Error() string {
return e.Op + " " + e.Path + ": " + e.Err.Error()
}
Custom Errors : Os
58. // Portable analogs of some common system call errors.
var ErrInvalid = errors.New("invalid argument")
var ErrPermission = errors.New("permission denied")
// PathError records an error and
// the operation and file path that caused it.
type PathError struct {
Op string
Path string
Err error
}
func (e *PathError) Error() string {
return e.Op + " " + e.Path + ": " + e.Err.Error()
}
Custom Errors : Os
59. func (f *File) WriteAt(b []byte, off int64) (n int, err error) {
if f == nil {
return 0, ErrInvalid
}
for len(b) > 0 {
m, e := f.pwrite(b, off)
if e != nil {
err = &PathError{"write", f.name, e}
break
}
n += m
b = b[m:]
off += int64(m)
}
return
}
Custom Errors : Os
60. func (f *File) WriteAt(b []byte, off int64) (n int, err error) {
if f == nil {
return 0, ErrInvalid
}
for len(b) > 0 {
m, e := f.pwrite(b, off)
if e != nil {
err = &PathError{"write", f.name, e}
break
}
n += m
b = b[m:]
off += int64(m)
}
return
}
Custom Errors : Os
61. func (f *File) WriteAt(b []byte, off int64) (n int, err error) {
if f == nil {
return 0, ErrInvalid
}
for len(b) > 0 {
m, e := f.pwrite(b, off)
if e != nil {
err = &PathError{"write", f.name, e}
break
}
n += m
b = b[m:]
off += int64(m)
}
return
}
Custom Errors : Os
62. func baa(f *file) error {
…
n, err := f.WriteAt(x, 3)
if _, ok := err.(*PathError) {
…
} else {
log.Fatalf(err)
}
}
Custom Errors : Os
63. …
if serr != nil {
if serr, ok := serr.(*PathError); ok &&
serr.Err == syscall.ENOTDIR {
return nil
}
return serr
…
Custom Errors : Os
64. …
if serr != nil {
if serr, ok := serr.(*PathError); ok &&
serr.Err == syscall.ENOTDIR {
return nil
}
return serr
…
Custom Errors : Os
65. f
e
h
W U
w k
M
w
P n
I
v
ZA
S
U
G
k
C
w
P
U
Mistake .
To Be Safe
Or Not To Be
7
66. f
e
h
W U
w k
M
w
P n
I
v
ZA
S
U
G
k
C
w
P
U
You Can’t Make
Everyone Happy
You Aren’t A Jar
Of Nutella
67. f
e
h
W U
w k
M
w
P n
I
v
ZA
S
U
G
k
C
w
P
U
Consider Concurrency
•If you provide a library someone
will use it concurrently
•Data structures are not safe for
concurrent access
•Values aren’t safe, you need to
create safe behavior around them
68. f
e
h
W U
w k
M
w
P n
I
v
ZA
S
U
G
k
C
w
P
U
Making It Safe
•Sync package provides behavior
to make a value safe (Atomic/
Mutex)
•Channels coordinate values
across go routines by permitting
one go routine to access at a
time
74. f
e
h
W U
w k
M
w
P n
I
v
ZA
S
U
G
k
C
w
P
U
Keeping It Unsafe
•Safety comes at a cost
•Imposes behaviors on consumer
•Proper API allows consumers to
add safety as needed
•Consumers can use channels or
mutexes
76. f
e
h
W U
w k
M
w
P n
I
v
ZA
S
U
G
k
C
w
P
U
Maps Are Unsafe By Design
•Often safety is unnecessary
•Enables consumers to
implement safety as needed
•Enables consumers to
implement safety as desired
77. f
e
h
W U
w k
M
w
P n
I
v
ZA
S
U
G
k
C
w
P
U
Biggsest
Mistake;
Not Makimg
Mistakes
78. f
e
h
W U
w k
M
w
P n
I
v
ZA
S
U
G
k
C
w
P
U
–Ed Catmull
Failure is a manifestation of learning
and exploration.
If you aren't experiencing failure
than you are making a far worse
mistake.
You are being driven by the desire
to avoid it.
79.
80. f
e
h
W U
w k
M
w
P n
I
v
ZA
S
U
G
k
C
w
P
U
@Spf13
Docker
Chief of Operations
&
Author of Hugo,
Cobra, Afero, Viper