What is Go?

  • Released in 2009
  • Developed with the Google service workload in mind
  • Big focus on Concurrency
  • Statically typed

Useful bits?

  • Developed for Google *
  • Easy Concurrency *
  • Quick to learn *
  • Built to be read *
  • Error handling. There are no exceptions *
  • Opinionated *
  • Statically compiled *
  • Gopath! *
  • go fmt

You could also say

  • Boring
  • Old Fashioned
  • No Package tools
  • No generics
  • Worse concurrency that erlang
  • Built for organisations much larger than you
  • Inflexible
  • No generics
  • Made for “mediocre” programers

YMMV

A larger list can be found at github.com/ksimka/go-is-not-good

Why Should you go?

  • Simple and boring
  • Concurrent
  • Testing
  • Excellent cross compilation
  • Simple deployment process
  • Operation tooling

Boring

  • Go is no longer exciting
  • Compatibility guarantees that hold
  • Error checking is painful but less painful than errors
  • Old enough that language rants are no longer hosted

Simple

Mandatory Hello World

package main

import "fmt"

func main() {
  fmt.Println("Hello, 世界")
}

Artificial example

func echoConn(conn net.Conn) {
  defer conn.Close()
  for {
    buf := make([]byte, 1024)
    size, err := conn.Read(buf)
    if err != nil {
      return
    }
    data := buf[:size]
    conn.Write(data)
  }
}
func main() {
  l, err := net.Listen("tcp", ":8081")
  if err != nil {
    panic(err)
  }
  defer l.Close()
  for {
    conn, err := l.Accept()
    if err != nil {
      panic(err)
    }
    echoConn(conn)
  }
}

Concurrency

Gophers doing things concurrently and in parallel

func main() {
  l, err := net.Listen("tcp", ":8081")
  if err != nil {
    panic(err)
  }
  defer l.Close()
  for {
    conn, err := l.Accept()
    if err != nil {
      panic(err)
    }
    go echoConn(conn)
  }
}

Racy behaviour

Go's race detector is awesome

go test -race ./...
go run -race main.go

Testing

Testing is table stakes. You can't get away for long without it. picture is dogs playing poker

func Fib(n int) int {
  if n < 2 {
    return n
  }
  return Fib(n-1) + Fib(n-2)
}
func TestFib(t *testing.T) {
  tests := []struct {
    name string
    arg int
    want int
  }{
    // TODO: Add test cases.
  }
  for _, tt := range tests {
    t.Run(tt.name, func(t *testing.T) {
      if got := Fib(tt.args.n); got != tt.want {
        t.Errorf("Fib() = %v, want %v", got, tt.want)
      }
    })
  }
}
func benchmarkFib(i int, b *testing.B) {
  for n := 0; n < b.N; n++ {
    Fib(i)
  }
}
...
func BenchmarkFib20(b *testing.B) {
  benchmarkFib(20, b)
}
func BenchmarkFib40(b *testing.B) {
  benchmarkFib(40, b)
}
$ go test -bench=. -benchmem
goos: windows
goarch: amd64
pkg: github.com/kcollasarundell/austnet/mondo/fib
BenchmarkFib1-4         1000000000               2.32 ns/op            0 B/op          0 allocs/op
BenchmarkFib2-4         200000000                6.66 ns/op            0 B/op          0 allocs/op
BenchmarkFib3-4         100000000               11.0 ns/op             0 B/op          0 allocs/op
BenchmarkFib20-4           30000             48133 ns/op               0 B/op          0 allocs/op
BenchmarkFib40-4               2         725500700 ns/op               0 B/op          0 allocs/op
BenchmarkFib10-4         5000000               378 ns/op               0 B/op          0 allocs/op
PASS
ok      github.com/kcollasarundell/austnet/mondo/fib    12.314s
$ benchcmp old.txt new.txt
benchmark           old ns/op     new ns/op     delta
BenchmarkConcat     523           68.6          -86.88%

benchmark           old allocs     new allocs     delta
BenchmarkConcat     3              1              -66.67%

benchmark           old bytes     new bytes     delta
BenchmarkConcat     80            48            -40.00%
package fib_test

import (
    "fmt"

    "github.com/kcollasarundell/hugo/tmpfiles/fib"
)
func exampleFib() {
    fmt.Println(fib.Fib(10))
    // Output: 55
}
% go test . -v
=== RUN   TestFib
--- PASS: TestFib (0.00s)
=== RUN   ExampleFib
--- PASS: ExampleFib (0.00s)
PASS
ok    github.com/kcollasarundell/hugo/tmpfiles/fib  0.010s

Cross compilation

cross eyed cat

CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o linux_amd64_binary
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -o linux_arm64_binary
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -o osx_amd64_binary
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o windows_amd64_binary

Simple deployment process

dog delivering parcel

FROM golang:latest AS builder

# Copy the code from the host and compile it
WORKDIR $GOPATH/src/github.com/username/repo
COPY . ./
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix nocgo -o /descriptiveName .

FROM scratch
COPY --from=builder /descriptiveName ./
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
ENTRYPOINT ["./descriptiveName"]
package main

import (
  "net/http"
  "github.com/gobuffalo/packr"
)

func main() {
  box := packr.NewBox("./templates")

  http.Handle("/", http.FileServer(box))
  http.ListenAndServe(":3000", nil)
}
packr && go build

Operation tooling

Cat performing operations

Always include pprof you never know when you're going to need

insert Demo of pprof that i am too lazy to record so go read @b0rk or @rakyll

What’s next

References

@kcollasarundell