Enums in Go Part Deux - Marshaling and Unmarshaling!

My blog post from yesterday made me think… how can you marshal and unmarshal enums in Go correctly? So I modified internal/coffee/coffee.go and added a MarshalText() and UnmarshalText() methods: package coffee import ( "fmt" "strings" ) //go:generate stringer -linecomment -type=Coffee type Coffee int const ( Drip Coffee = iota // drip coffee Latte // latte Breve // breve Cappuccino // cappuccino ) func (c Coffee) MarshalText() ([]byte, error) { return []byte(c.String()), nil } func (c *Coffee) UnmarshalText(text []byte) error { want := string(text) for i := 0; i < len(_Coffee_index)-1; i++ { name := _Coffee_name[_Coffee_index[i]:_Coffee_index[i+1]] if strings.EqualFold(name, want) { *c = Coffee(i) return nil } } return fmt.Errorf("invalid Coffee %q", want) } I then modified main.go to add a new type named Drink which expected a Coffee in its field: ...

2025-05-15

Enums in Go

I was asked yesterday how you implement enums in Go. I didn’t know, so I spent some time this morning learning how to do it. It turns out this is ridiculously easy to do and Go has the stringer tool which makes it super simple. (stringer codegens the code which prints your enum as a string.) Example I created a repo with the following structure: go.mod main.go internal/coffee/coffee.go internal/coffee/coffee_string.go internal/coffee/coffee.go looks like this: ...

2025-05-14

Workflow Musings

Getting things ready in advance I’ve been musing on how ridiculously useful it is to get things ready in advance. Especially the day before. Examples: Setting my workout clothes out. (I’m more likely to workout.) Packing my backpack with everything I need for work in the morning. (No hunting around in the morning for stuff when my brain is waking up and wasting 15 minutes or getting frustrated.) Setting everything I need to shower and brush my teeth after late-night roller skating in the guest bathroom. (So I can shower there and not wake up my wife.) It can sometimes feel silly in the moment, but I’ve found it makes it a lot easier to get the outcomes I want in life (e.g., working out, leaving on time, etc.) ...

2025-05-05

Weekly planning sessions

I’ve been doing a weekly planning session for the last five or so years. It is one of the most helpful things I do to organize my life, keep myself physically healthy, and moving forward. This process has become even more valuable to me since having a child, as I am now responsible for a lot more and time is more scarce. In this post, I’ll describe what I do, why it’s useful, and how I do it. ...

2025-02-01

Go Test Parallelism

This post documents some testing I did around whether unit tests in Golang are run in parallel. TLDR: Tests in a given package run serially, unless t.Parallel() is specified. To test this, I created two files in a directory named gotest: $ ls one_test.go two_test.go // one_test.go package main import ( "fmt" "testing" "time" ) func TestOne(t *testing.T) { fmt.Println("1 starts:", time.Now().String()) time.Sleep(5 * time.Second) fmt.Println("1 ends:", time.Now().String()) } // two_test.go package main import ( "fmt" "testing" "time" ) func TestTwo(t *testing.T) { fmt.Println("2 starts:", time.Now().String()) fmt.Println("2 ends:", time.Now().String()) } When I ran the tests, you can see that TestOne blocked TestTwo from running for five seconds: ...

2024-10-18

Go sub-slice gotchas

Thanks to Julia Evan’s latest post, I learned that creating new slices by sub-slicing an existing slice has an important caveat: They can sometimes use the same backing array! 😬 This is important to understand if you mutate the sub-slice. Take the below example, wherein we accidentally mutate s1! package main import ( "fmt" ) func main() { s1 := []int{0, 1, 2, 3, 4, 5} // len == 6, capacity == 6 s2 := s1[1:5] // len == 4, capacity == 5 // Modifies both s1 and s2, because they share the same backing array. s2[0] = 10 // Modifies both s1 and s2, because the length of s2 after adding the new // element does not exceed the capacity of the s2 slice. s2 = append(s2, 20) // Modifies only s2, because adding this element increases the length of s2 // beyond the capacity of the s2 slice. This allocats a new backing array, // copies all elements to it, and then appends 30 to it. So s1 does not get // modified. s2 = append(s2, 30) fmt.Println("s1", s1) fmt.Println("s2", s2) } Its output is: ...

2024-08-11

Unifi Network Application Upgrade

I run Unifi network gear in my house. As I have multiple wireless access points, I use a controller to manage them so clients can seamlessly roam between them. I run Unifi’s controller software as a Docker container (from here and here) to save myself from running Yet Another Appliance™ and paying for more hardware. I’ve been running v6 for a while, but recently upgraded to v8. These are my notes to myself for how I set this up. ...

2024-07-22

Configuring a Git pre-push hook to run unit tests

A coworker turned me onto this lovely technique the other day. You can use a git pre-push hook to run all of your Golang unit tests before pushing. To do this, make a the following file: $YOUR_REPO/.git/hooks/pre-push The file must be executable. The file’s contents should be: #!/bin/sh if ! go test ./... ; then echo echo "Rejecting commit. Unit tests failed." echo exit 1 fi Easy peasy.

2024-06-26

How to care for your Invisalign braces

I wore Invisalign braces from January of 2022 to early 2023. They require a lot of brushing and maintenance while wearing them. Here’s a few things I did along the way which made that easier. 1. Create brushing stations You’re going to brush and floss a lot. (Like a lot.) Anytime you eat or drink anything besides water you must remove the Invisalign trays, brush your teeth and trays, and then floss. ...

2024-04-14

Using git worktrees

Have you ever been developing on a feature branch and needed to look at a separate branch on the same repo? I have. When this happens, I normally do one of two things: git stash my changes and change branches This is annoying because it’s a lot of steps. I also have to remember which stash to pop when I come back to this branch. Stage all my changes and store them in a work-in-progress commit This is better than #1 but doesn’t let you view both branches simultaneously. But it’s still annoying if you have files you haven’t staged yet and don’t want to stage yet. Both these have drawbacks. For example, they don’t let me look at both branches at the same time in my text editor. ...

2024-03-27