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

Packages & Modules

Packages & Modules

Go packages & modules. Best practices. How to organise things. How to use new shiny modules.

Oleg Kovalov

June 14, 2019
Tweet

More Decks by Oleg Kovalov

Other Decks in Programming

Transcript

  1. In this talk 3 - About me - Packages -

    Modules - Summary - Q&A
  2. In this talk + extra 4 - About me -

    Packages - Modules - Summary - Q&A Interfaces
  3. I don’t have a Soundcloud...but 8 - Gopher for ~4

    years - Open-source contributor - Go-critic - static analysis tool - Go-advice - list of Go tips @ GoGoConf 2018
  4. I don’t have a Soundcloud...but 9 - Gopher for ~4

    years - Open-source contributor - Go-critic - static analysis tool - Go-advice - list of Go tips @ GoGoConf 2018 - Engineer at allegro.pl core team
  5. - Gopher for ~4 years - Open-source contributor - Go-critic

    - static analysis tool - Go-advice - list of Go tips @ GoGoConf 2018 - Engineer at allegro.pl core team Website: https://olegk.dev Twitter: @oleg_kovalov Github: @cristaloleg I don’t have a Soundcloud...but 10 Twitter & slides
  6. - Short and clear - May be abbreviated when the

    abbreviation is familiar Naming 14
  7. - Short and clear - May be abbreviated when the

    abbreviation is familiar - Abbrv name is easy to understand Naming 15
  8. Naming - Short and clear - May be abbreviated when

    the abbreviation is familiar - Abbrv name is easy to understand See standard packages for good examples - io (input and output) 16
  9. - Short and clear - May be abbreviated when the

    abbreviation is familiar - Abbrv name is easy to understand See standard packages for good examples - io (input and output) - strconv (string conversion) Naming 17
  10. - Short and clear - May be abbreviated when the

    abbreviation is familiar - Abbrv name is easy to understand See standard packages for good examples - io (input and output) - strconv (string conversion) - syscall (system call) Naming 18
  11. - If abbreviating is unclear - If name is ambiguous

    - snake_case or camelCase Naming don’ts 22
  12. Naming don’ts - If abbreviating is unclear - If name

    is ambiguous - snake_case or camelCase - psdb (Postgres? Parallel search?) 23
  13. - If abbreviating is unclear - If name is ambiguous

    - snake_case or camelCase - psdb (Postgres? Parallel search?) - srv (service? server? surviving?) Naming don’ts 24
  14. - If abbreviating is unclear - If name is ambiguous

    - snake_case or camelCase - psdb (Postgres? Parallel search?) - srv (service? server? surviving?) - buf (buffer? buffer what? WoW buff?) Naming don’ts 25
  15. - Avoid giving a package a name that is commonly

    used in client code Don’t steal good names 26
  16. - Avoid giving a package a name that is commonly

    used in client code - buf is a good variable name for a buffer - the buffered I/O package is called bufio (buffer + io) Don’t steal good names 27
  17. - Avoid giving a package a name that is commonly

    used in client code - buf is a good variable name for a buffer - the buffered I/O package is called bufio (buffer + io) - client is a good name for...client - but not a package name Don’t steal good names 28
  18. - Avoid giving a package a name that is commonly

    used in client code - buf is a good variable name for a buffer - the buffered I/O package is called bufio (buffer + io) - client is a good name for...client - but not a package name - imagine a replacement for it Don’t steal good names 29 c ? clnt ? cli ? clientHTTP ?
  19. These std packages are a good names thiefs: - path

    - path/filepath - net/url And you cannot use your variable url easily, oh :( ...and few from std packages 31
  20. ...and few from std packages 32 These std packages are

    a good names thiefs: - path - path/filepath - net/url And you cannot use your variable url easily, oh :( But there are good examples: - strings - bytes - net/urls instead of net/url looks good enough
  21. Most of the projects in Go are flat and this

    is fine Try to keep clean import paths 33
  22. Most of the projects in Go are flat and this

    is fine - Try to keep it clean github.com/foo/bar/pl/goava/src/main/code/go/validation Try to keep clean import paths 34
  23. Most of the projects in Go are flat and this

    is fine - Try to keep it clean github.com/foo/bar/pl/goava/src/main/code/go/validation - Avoid do src/ or pkg/ (in most cases) gitlab.com/foo/bar/pkg/process Try to keep clean import paths 35
  24. Write shy code - modules that don’t reveal anything unnecessary

    to other modules and that don’t rely on other modules' implementations. --- Dave Thomas (with Andy Hunt, he co-authored The Pragmatic Programmer) Shy packages 36
  25. Write shy code - modules that don’t reveal anything unnecessary

    to other modules and that don’t rely on other modules' implementations. --- Dave Thomas (with Andy Hunt, he co-authored The Pragmatic Programmer) And shy also means: 1 package = 1 thing Shy packages 37
  26. - Organize by functional responsibility - Don’t put all the

    type into models package Models package is a weak idea, so 46
  27. Models package is a weak idea, so 47 - Organize

    by functional responsibility - Don’t put all the type into models package - Keeping them in one gives nothing
  28. Models package is a weak idea, so 48 - Organize

    by functional responsibility - Don’t put all the type into models package - Keeping them in one gives nothing - In the end this will be a spaghetti code
  29. Models package is a weak idea, so 49 - Organize

    by functional responsibility - Don’t put all the type into models package - Keeping them in one gives nothing - In the end this will be a spaghetti code - (The same applies to the interfaces, implementations and so on)
  30. One of the possible structures 50 . ├── app ├──

    cmd │ └── <project-name> ├── blog ├── comment ├── mock ├── pkg │ ├── superpay │ └── validation ├── store │ └── postgres ├── user └── <project-name>.go
  31. One of the possible structures 51 . ├── app ├──

    cmd │ └── <project-name> ├── blog ├── comment ├── mock ├── pkg │ ├── superpay │ └── validation ├── store │ └── postgres ├── user └── <project-name>.go
  32. One of the possible structures 52 . ├── app ├──

    cmd │ └── <project-name> ├── blog ├── comment ├── mock ├── pkg │ ├── superpay │ └── validation ├── store │ └── postgres ├── user └── <project-name>.go
  33. One of the possible structures 53 mock.NewBlog(...) . ├── app

    ├── cmd │ └── <project-name> ├── blog ├── comment ├── mock ├── pkg │ ├── superpay │ └── validation ├── store │ └── postgres ├── user └── <project-name>.go
  34. One of the possible structures 54 . ├── app ├──

    cmd │ └── <project-name> ├── blog ├── comment ├── mock ├── pkg │ ├── superpay │ └── validation ├── store │ └── postgres ├── user └── <project-name>.go
  35. One of the possible structures 55 . ├── app ├──

    cmd │ └── <project-name> ├── blog ├── comment ├── mock ├── pkg │ ├── superpay │ └── validation ├── store │ └── postgres ├── user └── <project-name>.go
  36. One of the possible structures 56 . ├── app ├──

    cmd │ └── <project-name> ├── blog ├── comment ├── mock ├── pkg │ ├── superpay │ └── validation ├── store │ └── postgres ├── user └── <project-name>.go
  37. pkg, internal, misc or util Whatever works for your team

    and you One of the possible structures 57 . ├── app ├── cmd │ └── <project-name> ├── blog ├── comment ├── mock ├── pkg │ ├── superpay │ └── validation ├── store │ └── postgres ├── user └── <project-name>.go
  38. One of the possible structures 58 . ├── app ├──

    cmd │ └── <project-name> ├── blog ├── comment ├── mock ├── pkg │ ├── superpay │ └── validation ├── store │ └── postgres ├── user └── <project-name>.go
  39. Avoid util/common/base/helpers packages 65 - util.URLError - common.SanitizeMessage - base.EnsureAbsolutePath

    - helpers.IsEmail type URLError = app.URLError These packages say nothing!
  40. Avoid util/common/base/helpers packages 66 - util.URLError - common.SanitizeMessage - base.EnsureAbsolutePath

    - helpers.IsEmail type URLError = app.URLError var isEmail = validation.IsEmail These packages say nothing!
  41. - Use multiple files - Do not separate structs and

    their methods (not a C/C++) - It’s okay to store few structs in one file (it’s not Java after all) - And keep test files next to code store ├── postgres │ ├── postgres.go │ ├── user.go │ └── user_test.go └── redis ├── redis.go └── comments.go Inside the package 67
  42. - Use multiple files - Do not separate structs and

    their methods (not a C/C++) - It’s okay to store few structs in one file (it’s not Java after all) - And keep test files next to code store ├── postgres │ ├── postgres.go │ ├── user.go │ └── user_test.go └── redis ├── redis.go └── comments.go // type Comment and Comment.HasImage method Inside the package 68
  43. - Use multiple files - Do not separate structs and

    their methods (not a C/C++) - It’s okay to store few structs in one file (it’s not Java after all) - And keep test files next to code store ├── postgres │ ├── postgres.go │ ├── user.go // type User and type Credentials │ └── user_test.go └── redis ├── redis.go └── comments.go Inside the package 69
  44. - Use multiple files - Do not separate structs and

    their methods (not a C/C++) - It’s okay to store few structs in one file (it’s not Java after all) - And keep test files next to code store ├── postgres │ ├── postgres.go │ ├── user.go │ └── user_test.go └── redis ├── redis.go └── comments.go Inside the package 70
  45. Is this storage? Stats? Cache? Benchmark suites? 71 postgres ├──

    postgres.go ├── user.go ├── stats.go └── user_test.go redis ├── redis.go ├── cache.go └── comments.go
  46. Is this storage? Stats? Cache? Benchmark suites? 72 postgres ├──

    postgres.go ├── user.go ├── stats.go └── user_test.go redis ├── redis.go ├── cache.go └── comments.go cache ├── redis │ └── redis.go store ├── postgres │ ├── postgres.go │ ├── user.go │ └── user_test.go └── redis ├── redis.go └── comments.go
  47. - Main packages are not importable - Don’t export from

    main, it’s useless What about main package? 75
  48. - Main packages are not importable - Don’t export from

    main, it’s useless - main package is hard to test (see above) What about main package? 76
  49. Keep your main simple and testable 78 package main func

    init() { config.Load() } func main() { os.Exit(realMain()) } func realMain() int { log.SetOutput(ioutil.Discard) // ... }
  50. - 1 init() per package (pretty please) - Do not

    depend on init’s call order Keep your init() light 82
  51. - 1 init() per package (pretty please) - Do not

    depend on init’s call order - No heavy tasks Keep your init() light 83
  52. - 1 init() per package (pretty please) - Do not

    depend on init’s call order - No heavy tasks - Preferably no I/O, especially blocking Keep your init() light 84
  53. - 1 init() per package (pretty please) - Do not

    depend on init’s call order - No heavy tasks - Preferably no I/O, especially blocking - Better to not have it at all Keep your init() light 85
  54. # Python is quite flexible # try: import foo except:

    print(‘good try lol’) panic in init() 86
  55. # Python is quite flexible # try: import foo except:

    print(‘good try lol’) // Go isn’t so… // package foo func init() { iCanPanicAndYouCanDoNothing() } panic in init() 87
  56. // but wait, it can be flexible... package foo func

    Init() { iCanPanicButOoooohhYouCanCatchMe() } panic in init() 88
  57. panic in init() // but wait, it can be flexible...

    package foo func Init() { iCanPanicButOoooohhYouCanCatchMe() } ----------- package bar func ... { defer func() { if r := recover(); r != nil { // process a panic } }() foo.Init() } 89
  58. package foo func (s *Service) Process() { // let’s start

    a sweet goroutine go func() { iCanPanicAndYouCanDoNothing() }() } I heard you like goroutines 90
  59. package foo func (s *Service) Process() { // let’s start

    a sweet goroutine go func() { iCanPanicAndYouCanDoNothing() }() } ----------- package bar func ... { // but internal goroutine // isn’t accessible here :( s.Process() } I heard you like goroutines 91
  60. package foo func (s *Service) Process() { go func() {

    defer func() { // recovery process }() iCanPanicAndAuthorWillRecover() }() } I heard you like goroutines 92
  61. package foo func (s *Service) Process() { iCanPanicButItDoesntMatter() } -----------

    package bar func ... { go func() { defer func() { // user defined recovery process }() s.Process() }() } I heard you like goroutines 94
  62. - Exported package variable is mutable by everyone - You

    have no power to control other packages Global state 97
  63. - Exported package variable is mutable by everyone - You

    have no power to control other packages - Tight coupling makes code less flexible Global state 98
  64. - Exported package variable is mutable by everyone - You

    have no power to control other packages - Tight coupling makes code less flexible - You cannot mock a package! Global state 99
  65. - Exported package variable is mutable by everyone - You

    have no power to control other packages - Tight coupling makes code less flexible - You cannot mock a package! - Logger, metrics? - mmmmaybe Global state 100
  66. - Exported package variable is mutable by everyone - You

    have no power to control other packages - Tight coupling makes code less flexible - You cannot mock a package! - Logger, metrics? - mmmmaybe - Config, flags? - better no Global state 101
  67. - Exported package variable is mutable by everyone - You

    have no power to control other packages - Tight coupling makes code less flexible - You cannot mock a package! - Logger, metrics? - mmmmaybe - Config, flags? - better no - DB driver? - NONONO Global state 102
  68. - Exported package variable is mutable by everyone - You

    have no power to control other packages - Tight coupling makes code less flexible - You cannot mock a package! - Logger, metrics? - mmmmaybe - Config, flags? - better no - DB driver? - NONONO Global state 103 by the way unexported variables are also bad :(
  69. - Should be simply a container for consts, types, funcs

    Good package == stateless container 105
  70. - Should be simply a container for consts, types, funcs

    - Zero variables is brilliant (both exported and unexported) Good package == stateless container 106
  71. - Should be simply a container for consts, types, funcs

    - Zero variables is brilliant (both exported and unexported) - Documentation and tests :) Good package == stateless container 107
  72. - log.Printf A safe action, might be noisy, but harmless

    - log.Panicf Not idiomatic, but in an edge case might be fine Logging is cool, but... 110
  73. Logging is cool, but... 111 - log.Printf A safe action,

    might be noisy, but harmless - log.Panicf Not idiomatic, but in an edge case might be fine - log.Fatalf Evil has no boundaries, omit it, no one can survive os.Exit(1) (even defer)
  74. - doc.go is a README of your package - Run

    godoc locally to see how it looks Forgotten doc.go 114
  75. Forgotten doc.go 115 - doc.go is a README of your

    package - Run godoc locally to see how it looks - Also add package documentation // Package bytes implements functions for the manipulation of byte slices. // It is analogous to the facilities of the strings package. package bytes
  76. Be conservative in what you send, be liberal in what

    you accept (с) Postel’s Law, Wikipedia Accept interfaces, return structs 118
  77. Be conservative in what you send, be liberal in what

    you accept (с) Postel’s Law, Wikipedia TCP architecture: Accept interfaces, return structs 119
  78. Accept interfaces, return structs Be conservative in what you send,

    be liberal in what you accept (с) Postel’s Law, Wikipedia TCP architecture: - We don’t care about how packets go through network 120
  79. Be conservative in what you send, be liberal in what

    you accept (с) Postel’s Law, Wikipedia TCP architecture: - We don’t care about how packets go through network - but we expect them in a well defined order to process Accept interfaces, return structs 121
  80. func FetchUsers(db Store) ([]*User, error) { ... } - We

    don’t care what is inside: Postgres, Redis, plain file or a mock Accept interfaces, return structs 123
  81. func FetchUsers(db Store) ([]*User, error) { ... } - We

    don’t care what is inside: Postgres, Redis, plain file or a mock - Until it has desired API for us Accept interfaces, return structs 124
  82. func FetchUsers(db Store) ([]*User, error) { ... } - We

    don’t care what is inside: Postgres, Redis, plain file or a mock - Until it has desired API for us - But we know for sure that we need a specific struct User Accept interfaces, return structs 125
  83. func FetchUsers(db Store) ([]*User, error) { ... } - We

    don’t care what is inside: Postgres, Redis, plain file or a mock - Until it has desired API for us - But we know for sure that we need a specific struct User Tip: return error as interface, not as a struct Accept interfaces, return structs 126
  84. - There is the “Java-style” interface declaration - Define an

    interface Where to declare interfaces? 129
  85. - There is the “Java-style” interface declaration - Define an

    interface - Create a type that implements it Where to declare interfaces? 130
  86. Where to declare interfaces? 131 - There is the “Java-style”

    interface declaration - Define an interface - Create a type that implements it - Often you have one interface and one implementation
  87. Where to declare interfaces? - There is the “Java-style” interface

    declaration - Define an interface - Create a type that implements it - Often you have one interface and one implementation That’s not a “Go style” ʕ◔ϖ◔ʔ 132
  88. - Go interfaces are in the package that uses it

    Where to declare Go interfaces? 134
  89. - Go interfaces are in the package that uses it

    - And this makes Go code easy to extend Where to declare Go interfaces? 135
  90. - GOPATH - godep - gopkg.in - glide - vendor

    dir Pre-modules era (2015-...) Go 1.5 143
  91. - GOPATH - godep - gopkg.in - glide - vendor

    dir - dep Pre-modules era (2017-...) 144
  92. - A module is a collection of Go packages that

    are versioned as a single unit What is a module? 147
  93. - A module is a collection of Go packages that

    are versioned as a single unit - A module is a tree/directory of Go source files with a go.mod file in the root ├── cmd │ └── main.go ├── consum │ ├── consum.go │ └── handlers.go ├── produce │ ├── handlers.go │ ├── handlers_test.go │ └── processing.go ├── go.mod └── go.sum What is a module? 148
  94. - A module is a collection of Go packages that

    are versioned as a single unit - A module is a tree/directory of Go source files with a go.mod file in the root ├── cmd │ └── main.go ├── consum │ ├── consum.go │ └── handlers.go ├── produce │ ├── handlers.go │ ├── handlers_test.go │ └── processing.go ├── go.mod └── go.sum What is a module? 149 Can leave outside of GOPATH
  95. Import compatibility rule 151 - If an old and a

    new package have the same import path - the new package must be backwards-compatible with the old package
  96. Import compatibility rule 152 - If an old and a

    new package have the same import path - the new package must be backwards-compatible with the old package import ( "github.com/golang/hello/hey" // v0 or v1 "github.com/golang/hello/v2/hey" )
  97. How to enable modules? 153 - auto(default) - on -

    off $ export GO111MODULE=on $ go build ./...
  98. go mod commands 154 Usage: go mod <command> [arguments] The

    commands are: download download modules to local cache edit edit go.mod from tools or scripts graph print module requirement graph init initialize new module in current directory tidy add missing and remove unused modules vendor make vendored copy of dependencies verify verify dependencies have expected content why explain why packages or modules are needed
  99. Let’s start with modules $ mkdir /mygo/hello $ cd /mygo/hello

    $ go mod init github.com/golang/hello go: creating new go.mod: module github.com/golang/hello $ ls go.mod 156
  100. Let’s start with modules 157 $ mkdir /mygo/hello $ cd

    /mygo/hello $ go mod init github.com/golang/hello go: creating new go.mod: module github.com/golang/hello $ ls go.mod $ cat go.mod module github.com/golang/hello
  101. $ git clone https://github.com/go-gitea/gitea Cloning into gitea... $ cd gitea

    $ go mod init go: creating new go.mod: module github.com/go-gitea/gitea go: copying requirements from Gopkg.lock Convert to modules 159
  102. Convert to modules 160 $ git clone https://github.com/go-gitea/gitea Cloning into

    gitea... $ cd gitea $ go mod init go: creating new go.mod: module github.com/go-gitea/gitea go: copying requirements from Gopkg.lock $ go mod tidy go: extracting github.com/PuerkitoBio/goquery v1.5.0 go: extracting github.com/blevesearch/bleve v0.7.0 go: finding github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57 go: finding golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961 go: finding golang.org/x/time v0.0.0-20181108054448-85acf8d2951c go: downloading github.com/facebookgo/grace v0.0.0-20180706040059-75cf19382434 ...
  103. How to read go.mod? module github.com/hashicorp/consul go 1.12 require (

    github.com/NYTimes/gziphandler v1.0.1 <...>/datadog-go v0.0.0-20160329135253-cc2f4770f4d6 // indirect github.com/go-redis/redis v6.15.2+incompatible ... ) 162
  104. How to read go.mod? 164 module github.com/hashicorp/consul go 1.12 require

    ( github.com/NYTimes/gziphandler v1.0.1 <...>/datadog-go v0.0.0-20160329135253-cc2f4770f4d6 // indirect github.com/go-redis/redis v6.15.2+incompatible ... ) replace github.com/hashicorp/consul/api => ./api // can be fork or branch
  105. How to read go.mod? 165 module github.com/hashicorp/consul go 1.12 require

    ( github.com/NYTimes/gziphandler v1.0.1 <...>/datadog-go v0.0.0-20160329135253-cc2f4770f4d6 // indirect github.com/go-redis/redis v6.15.2+incompatible ... ) replace github.com/hashicorp/consul/api => ./api // can be fork or branch exclude github.com/not-a-hacker/supergeil v1270.0.1
  106. How to read go.sum? 168 cloud.google.com/go v0.26.0 h1:e0WKqKTd5BnrG8aKH3J3h+QvEIQtSUcf2n5UZ5ZgLtQ= cloud.google.com/go v0.26.0/go.mod

    h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/Azure/azure-sdk-for-go v16.0.0+incompatible h1:gr1qKY/Ll72VjFTZmaBwRK1yQHAxCnV25ekOKroc9ws= github.com/Azure/azure-sdk-for-go v16.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= // you might want to use go mod verify
  107. # same (@latest is default for 'go get') $ go

    get github.com/gorilla/mux@latest go get understands versioning 170
  108. # same (@latest is default for 'go get') $ go

    get github.com/gorilla/mux@latest # records v1.6.2 $ go get github.com/gorilla/[email protected] $ go get github.com/gorilla/mux@e3702bed2 go get understands versioning 171
  109. go get understands versioning # same (@latest is default for

    'go get') $ go get github.com/gorilla/mux@latest # records v1.6.2 $ go get github.com/gorilla/[email protected] $ go get github.com/gorilla/mux@e3702bed2 # records v0.0.0-20180517173623-c85619274f5d $ go get github.com/gorilla/mux@c856192 172
  110. go get understands versioning 173 # same (@latest is default

    for 'go get') $ go get github.com/gorilla/mux@latest # records v1.6.2 $ go get github.com/gorilla/[email protected] $ go get github.com/gorilla/mux@e3702bed2 # records v0.0.0-20180517173623-c85619274f5d $ go get github.com/gorilla/mux@c856192 # records current meaning of master $ go get github.com/gorilla/mux@master
  111. # go and get a repo $ go get github.com/go-gitea/gitea

    $ cd ~/go/src/github.com/go-gitea/gitea go mod why 174
  112. # go and get a repo $ go get github.com/go-gitea/gitea

    $ cd ~/go/src/github.com/go-gitea/gitea # set a env var and check why do we use this dependency $ export GO111MODULE=on $ go mod why github.com/RoaringBitmap/roaring go mod why 175
  113. go mod why 176 # go and get a repo

    $ go get github.com/go-gitea/gitea $ cd ~/go/src/github.com/go-gitea/gitea # set a env var and check why do we use this dependency $ export GO111MODULE=on $ go mod why github.com/RoaringBitmap/roaring # github.com/RoaringBitmap/roaring code.gitea.io/gitea/modules/indexer github.com/blevesearch/bleve github.com/blevesearch/bleve/index/scorch github.com/RoaringBitmap/roaring
  114. # install $ go get github.com/rogpeppe/gohack $ gohack get example.com/foo/bar

    # will clone repo into $HOME/gohack/example.com/foo/bar # will add replace statement $ cat go.mod replace example.com/foo/bar => /home/rog/gohack/example.com/foo/bar gohack by Roger Peppe 178
  115. gohack by Roger Peppe 179 # install $ go get

    github.com/rogpeppe/gohack $ gohack get example.com/foo/bar # will clone repo into $HOME/gohack/example.com/foo/bar # will add replace statement $ cat go.mod replace example.com/foo/bar => /home/rog/gohack/example.com/foo/bar # to remove specific replace: $ gohack undo example.com/foo/bar # to remove all replaces: $ gohack undo
  116. - Usability ~ Reusability - Make application team-friendly - Keep

    library easy to use Application != Library 187
  117. - Usability ~ Reusability - Make application team-friendly - Keep

    library easy to use Animated-emoji-colorful output in app - cool, whatever Application != Library 188
  118. - Usability ~ Reusability - Make application team-friendly - Keep

    library easy to use Animated-emoji-colorful output in app - cool, whatever Math library with zero allocation HTTP router - thanks, but no Application != Library 189
  119. - Simple and shy packages - Clear naming is important

    - No global state at all Summary 194
  120. Summary - Simple and shy packages - Clear naming is

    important - No global state at all - Interfaces are for abstraction 195
  121. Summary 196 - Simple and shy packages - Clear naming

    is important - No global state at all - Interfaces are for abstraction - Modules == Go ecosystem 2.0
  122. Summary 197 - Simple and shy packages - Clear naming

    is important - No global state at all - Interfaces are for abstraction - Modules == Go ecosystem 2.0 - App != Lib
  123. Q&A

  124. - Ben Johnson @benbjohnson - Dave Cheney @davecheney - Jaana

    B. Dogan @rakyll - Go team ʕ◔ϖ◔ʔ - all the friends \o/ Thanks 199