Skip to content

Instantly share code, notes, and snippets.

@au-phiware
Created November 14, 2025 06:26
Show Gist options
  • Select an option

  • Save au-phiware/bd303d908140869dfb0ed1079fe9167a to your computer and use it in GitHub Desktop.

Select an option

Save au-phiware/bd303d908140869dfb0ed1079fe9167a to your computer and use it in GitHub Desktop.
title sub_title author theme
Introduction to Go
A Masterclass
MachShip
name override
catppuccin-mocha
intro_slide
title
font_size
4

Welcome to Go

Go is a statically typed, compiled language designed at Google
for building simple, reliable, and efficient software.

Key traits:

  • Fast compilation
  • First-class concurrency
  • Garbage collected
  • Minimalist design philosophy

Functional Options

Problem: How to construct a value with many dependencies and modalities?

 ┌───────────────┐                               
 │  I know how   │                               
 │ to configure  ├───────┐                       
 │ http handler  │       │                       
 └───────────────┘       │                       
 ┌───────────────┐       │                       
 │  I know how   │       │                       
 │ to configure  ├───────┤                       
 │  the logger   │       │      ┌───────────────┐
 └───────────────┘       │      │               │
 ┌───────────────┐       ├─────►│ Configure App │
 │  I also want  │       │      │               │
 │ to configure  ├───────┤      └───────────────┘
 │ http handlers │       │                       
 └───────────────┘       │                       
 ┌───────────────┐       │                       
 │  I know how   │       │                       
 │ to configure  ├───────┘                       
 │  the logger,  │                               
 │     also      │                               
 └───────────────┘                               

Modules and Packages

Go distributes code in modules.

.
├── AGENTS.md
├── README.md
├── go.mod
├── go.sum
├── health.go
├── logger.go
├── telemetry.go
.
.
.

Contains:

  • go.mod
  • *.go files
  • optionally, many packages

Code is organised into packages.

package main

import (
    "context"
    "log/slog"

    "github.com/ms/o11y"
)

const ...

var ...

type ...

func main() { ... }
  • Limited to one per directory
  • Only exported members are capitalised

Types: Primitives

Go is 'batteries-included' has a full list of primitives,
some platform dependent.

  • bool
  • int/uint/int8/uint8/int16/uint16/int32/uint32/int64/uint64
  • byte/uintptr
  • float32/float64
  • complex64/complex128
  • string/rune
  • any/nil
  • func
  • map[Type]Type1
  • [123]Type/[]Type

The Go standard library (stdlib) is also 'batteries-included'.

Types: Functions

func Name() {
    fmt.Println("side effects only :O")
}

func Name(a Type, b AnotherType) ReturnType { /* ... */ }

func Name(a, b Type, c AnotherType) { /* ... */ }

func Name() (a ReturnType, b AnotherReturnType) {
    /* ... */
}

Structs

Go uses structs to compose types from other types.

The o11y package contains:

package o11y

type Health struct {
    ready    bool
    checkers []func(context.Context)
}

...which can be imported into another package:

import "github.com/machship/o11y"

var h o11y.Health

health := o11y.Health{}

var healthPtr *o11y.Health = &health

Zero Values

Every type has a zero value

Type Zero
bool false
int 0
float64 0.0
string ""
[]Type nil
func nil
Health { ready: false, checkers: nil }
*Health nil
package o11y

type Health struct {
    ready    bool
    checkers []func(context.Context)
}

Constructors

Almost always start with New and return a pointer.
Should always be infallible.

func NewHealth(...) *Health {
    return &Health{}
}
func NewHealth(
    ready bool,
    checkers ...func(context.Context),
) *Health {
    return &Health{
        ready: ready,
        checkers: checkers,
    }
}
type Config struct {
    Ready bool
}

func NewHealth(config Config) *Health ...

Functional Options Pattern

func NewHealth(options ...func(*Health)) *Health {
    h := &Health{}

    for _, opt := range options {
        opt(h)
    }

    return h
}

func WithChecker(fn func(context.Context)) func(*Health) {
    return func(h *Health) {
        h.checkers = append(h.checkers, fn)
    }
}
h := o11y.NewHealth(
    o11y.WithChecker(isDatabaseUp),
    o11y.WithChecker(isRouterReady),
)

More Functional Options

func WithOptions(options ...func(*Health)) func(*Health) {
    return func(h *Health) {
        for _, opt := range options {
            opt(h)
        }
    }
}

func NewHealth(options ...func(*Health)) *Health {
    h := &Health{}
    WithOptions(options...)(h)
    return h
}
commonOptions := o11y.WithOptions(
    o11y.WithChecker(isDatabaseUp),
    o11y.WithChecker(isRouterReady),
)

Type Aliases & Newtypes

Type alias give a alternative name to a type.

type ReadyState = bool

Type Aliases & Newtypes

Type alias give a alternative name to a type.

type ReadyState = bool

A newtype defines a new type with an underlying type.

type Checker func(context.Context)

type Option func(*Health)

The underlying type and newtype can be converted from one to the other but they are completely different types.

Type Aliases & Newtypes: Continued

func WithChecker(fn Checker) Option {
    return func(h *Health) {
        h.AddChecker(fn)
    }
}

func WithOptions(options ...Option) Option {
    return func(h *Health) {
        for _, opt := range options {
            opt(h)
        }
    }
}

func NewHealth(options ...Option) *Health {
    h := &Health{}
    WithOptions(options)(h)
    return h
}

Methods on Structs

Structs also encapsulate data and behaviour.

func (h *Health) AddChecker(fn Checker) {
    h.checkers = append(h.checkers, fn)
}

func (h Health) Ready() bool {
    return h.ready
}

Hint:
Use pointer receivers (*Health) for mutation,
value receivers for read-only.

Interfaces: Implicit & Structural

Go interfaces are satisfied implicitly. No implements keyword.

type Handler interface {
    ServeHTTP(w ResponseWriter, r *Request)
}
func (h *Health) ServeHTTP(
    w http.ResponseWriter,
    r *http.Request,
) {
    switch r.Method {
    case http.MethodGet:
        handleGet(h, w, r)
    case http.MethodOptions:
        handleOptions(h, w, r)
    default:
        w.WriteHeader(http.StatusMethodNotAllowed)
    }
}

Any type with ServeHTTP(ResponseWriter, *Request) is an http.Handler.

Functional Options: Part Two

type Option interface {
	Apply(*Health)
}

type Options []Option

func (opts Options) Apply(h *Health) {
	for _, opt := range opts {
		opt.Apply(h)
	}
}

type OptionFunc func(*Health)

func (fn OptionFunc) Apply(h *Health) {
    fn(h)
}

func NewHealth(options ...Option) *Health {
    h := &Health{}
    Options(options).Apply(h)
    return h
}

Functional Options: Part Two

type Config struct {
    Ready bool
    Checkers []Checker
}

func (c Config) Apply(h *Health) {
    h.ready = c.Ready
    h.AddChecker(c.Checkers...)
}

Functional Options: Part Three

Problem: How to construct a value with multiple phases?

Sometimes you need to pass or construct an intermediary value, validate it and then construct your final value and discard the rest.

func WithComplexThing(thing Complex) (Option, error) {
    if err := validate(thing); err != nil {
        return nil, err
    }
    return func(h *Health) { /* ... */ }, nil
}

Functional Options: Part Three

type app struct {
	config Config
    health *o11y.Health
}

type appConfig struct {
	*app
	healthOptions []o11y.Option
}

type Option interface {
	Apply(*appConfig)
}

func New(opts ...Option) *app {
	builder := &appConfig{app: &app{}}
	Options(opts).Apply(builder)
	app.health = o11y.NewHealth(builder.healthOptions...)
	return builder.app
}

Resources

Official:

This codebase:

  • github.com/machship/o11y - Observability patterns
  • Real-world examples of concurrency, generics, interfaces

Community:

Thank You!

Questions?

Code examples from: github.com/machship/o11y

  • Telemetry management with OpenTelemetry
  • Generic health checks
  • Concurrent resource cleanup
  • Middleware patterns

Start coding: The best way to learn Go is to write Go.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment