Go Learning Resources

I've only dabbled with Go in my spare time, but compiled this list of resources I found helpful.

Getting Started

  • Take A Tour of Go
  • brew update && brew install golang
  • Install the extension for VS Code
    • Add these to your User Settings (⌘ + ,):

      "go.testOnSave": true,
      "go.coverOnSave": true,
      "go.addTags": {
      "tags": "json",
      "options": "json=omitempty",
      "promptForTags": false,
      "transform": "camelcase"
      }

  • Work through How to Write Go Code
  • Fall in love with the tooling, watch Go Tooling in Action
  • Take a codewalk written by the creators, First-Class Functions in Go

First impressions

Pros

  • ⚡ Feedback cycle is fast, feels like a dynamic language
  • 🔨 Tooling is excellent, here's what I mean
  • 🤓 Encourages you to look at source code since it's all pulled down, which is an excellent way to learn new tricks
  • 📖 Automatic, simple, offline documentation with godoc. It can even have testable examples! More info, Godoc
  • 📏 In Scala, seemingly simple bits of code can be written many different ways. In code review, comments relate to formatting or refactoring into something easier to read. In Go, there's one way to code most things and one way to format, gofmt. Reviewers can concentrate on other things

Cons

  • ❌ No generics (here's why), though it's under consideration
  • 😫 Mocking can get verbose
  • 💣 signal SIGSEGV: segmentation violation brings back memories, painful memories
  • 😭 gofmt uses tabs

Dependency Injection

Create structs that define their dependencies as interfaces so they can be mocked in tests,


type HTTPClient interface {
Get(string) (*http.Response, error)
}
type Handler struct {
Client HTTPClient
}
func (h *Handler) Handle(url string) (string, error) {
res, err := h.Client.Get(url)
...
}

In main(),


func main() {
// http.Client from standard library implicitly satisfies HTTPClient interface
handler := Handler{Client: &http.Client{}}
...
}

And in your test,


type httpMock struct{}
// Satisfy HTTPClient interface, modify to return whatever you need
func (m *httpMock) Get(url string) (resp *http.Response, err error) {
return &http.Response{}, nil
}
func TestGetError(t *testing.T) {
handler := Handler{Client: &httpMock{}}
...
}

Testing

Table-driven tests are common and made simple with existing Go constructs.


func TestIsValidRtn(t *testing.T) {
cases := map[string]struct {
input string
expected bool
}{
"zero value": {"", false},
"< 9 digits": {"00000000", false},
"valid": {"010000003", true},
}
for k, c := range cases {
actual, _ := IsValidRtn(c.input)
if actual != c.expected {
t.Errorf("IsValidRtn(%q) == %v, expected %v. %v", c.input, actual, c.expected, k)
}
}
}

Even if you have one test case, consider writing it like this to easily add more later. More info, Table Driven Tests, Advanced Testing with Go, Testify.

Errors

Always handle errors, do not ignore them,


// 😎
result, err := couldReturnError()
if err != nil {
...
}
// 🙁
result, _ := couldReturnError()

If you're worried about code riddled with error checks, read this. Prefer errors over panics, more info

Channels

Identify separate pieces of work and compose their interactions with channels. Separating processes makes programs simple to follow and leads to better design. More info, Pipelines, Concurrency Is Not Parallelism

Up your game

Stay up to date

Get notified when I publish. Unsubscribe at any time.