Go Tips ¶
Here lies my collection of random things I've learned how to do in Go.
Helpful Links ¶
Nice Logs ¶
I use slog for everything. In production, I use a custom Google Cloud Run handler, but in development, I like to output everything in pretty colors. charmbracelet/log to the rescue!
package main
import (
"log/slog"
"os"
charmlog "github.com/charmbracelet/log"
)
func main() {
isDev := flag.Bool("dev", false, "enable development mode")
flag.Parse()
var handler slog.Handler
if *isDev {
handler = charmlog.New(os.Stderr)
} else {
handler = slog.NewJSONHandler(os.Stdout)
}
slog.SetDefault(slog.New(handler))
slog.Info("hello, world!")
slog.Warn("ummm")
slog.Error("oh no")
}
Shuffling a List of Items on Repeat ¶
2026-02-28
Think about when you want to show a list of loading messages to a user. You don't want to show them in the same order, every time, and you don't want to show an item multiple times when the list hasn't been exhausted yet.
Here's how I do it.
package main
import (
"fmt"
"math/rand/v2"
"time"
)
func main() {
messages := []string{"Reticulating splines", "Cohorting Exemplars", "Deunionizing Bulldozers", "Resolving GUID Conflict"}
var buffer []string
var message string
tick := time.Tick(time.Second)
done := time.After(10 * time.Second)
for {
if len(messages) == 0 {
messages, buffer = buffer, messages
}
rand.Shuffle(len(messages), func(i, j int) {
messages[i], messages[j] = messages[j], messages[i]
})
message, messages = messages[0], messages[1:]
buffer = append(buffer, message)
select {
case <-tick:
fmt.Println(message)
case <-done:
fmt.Println("done!")
return
}
}
}

Time Formats ¶
Created 2025-04-28, Updated 2026-02-28
There are a lot of time formats that I find myself frequently using.
I get the impression these are not well-known, even though they're built into the standard library.
package main
import (
"fmt"
"time"
)
func Example() {
formats := []string{
time.DateOnly,
time.TimeOnly,
time.RFC3339,
time.Kitchen,
}
now := time.Now()
for _, format := range formats {
fmt.Println(now.Format(format))
}
// Output:
// 2009-11-10
// 23:00:00
// 2009-11-10T23:00:00Z
// 11:00PM
}
Marking a Context as Done with a Signal ¶
2025-07-12
The signal package has a useful function called NotifyContext that will notify the Done channel of the Context when one of the listed signals is received by the running process.
Watch out! When you use this, the exit method will no longer be called when an interrupt is received, so you'll need to call it yourself.
package main
import (
"context"
"fmt"
"os"
"os/signal"
)
func main() {
ctx, _ := signal.NotifyContext(context.Background(), os.Interrupt)
fmt.Println("press Ctrl-C to exit...")
// waits
<-ctx.Done()
os.Exit(0)
}
Emulating Closed Sum-Type / Discriminated Unions using the AsAny Pattern ¶
2025-05-04
OpenAI's Go library makes extensive use of this, and I quite like it.
The trick is to use a sealed-interface union wrapper. It's a struct with mutually-exclusive pointer fields plus an unexported marker interface. You don't use the wrapper directly; use constructors to instantiate new instances.
package main
import "fmt"
type Dog struct{}
func NewDog(d Dog) PetUnion {
return PetUnion{OfDog: &d}
}
func (Dog) bark() { fmt.Println("bark") }
type Cat struct{}
func NewCat(c Cat) PetUnion {
return PetUnion{OfCat: &c}
}
func (Cat) meow() { fmt.Println("meow") }
// keep this private
type anyPet interface {
implAnyPetUnion()
}
func (Dog) implAnyPetUnion() {}
func (Cat) implAnyPetUnion() {}
type PetUnion struct {
OfDog *Dog
OfCat *Cat
}
func (u PetUnion) AsAny() anyPet {
switch {
case u.OfDog != nil:
return u.OfDog
case u.OfCat != nil:
return u.OfCat
}
return nil
}
func Example() {
pet := NewDog(Dog{})
switch v := pet.AsAny().(type) {
case *Dog:
v.bark()
case *Cat:
v.meow()
}
// Output:
// bark
}
Tickers / Recurring Tasks ¶
Created 2025-04-28, Updated 2026-02-28
package main
import (
"fmt"
"time"
)
func main() {
tick := time.Tick(time.Second)
after := time.After(3 * time.Second)
for {
select {
case <-tick:
fmt.Println("ticked!")
case <-after:
fmt.Println("goodbye!")
return
}
}
}

Capitalize Words ¶
2025-04-28
package main
import (
"fmt"
"golang.org/x/text/cases"
"golang.org/x/text/language"
)
func Capitalize(s string) string {
return cases.Title(language.English).String(s)
}
func ExampleCapitalize() {
fmt.Println(Capitalize("abcdef"))
fmt.Println(Capitalize("hi there"))
fmt.Println(Capitalize("CAPITALIZED"))
// Output:
// Abcdef
// Hi There
// Capitalized
}