Preventing accidental struct copies in Go

By default, Go copies values when you pass them around. But sometimes, that can be undesirable. For example, if you accidentally copy a mutex and multiple goroutines work on separate instances of the lock, they won’t be properly synchronized. In those cases, passing a pointer to the lock avoids the copy and works as expected. Take this example: passing a sync.WaitGroup by value will break things in subtle ways: func f(wg sync.WaitGroup) { // ... do something with the waitgroup } func main() { var wg sync.WaitGroup f(wg) // oops! wg is getting copied here! } sync.WaitGroup lets you wait for multiple goroutines to finish some work. Under the hood, it’s a struct with methods like Add, Done, and Wait to sync concurrently running goroutines. ...

April 21, 2025

Go 1.24's "tool" directive

Go 1.24 added a new tool directive that makes it easier to manage your project’s tooling. I used to rely on Make targets to install and run tools like stringer, mockgen, and linters like gofumpt, goimports, staticcheck, and errcheck. Problem is, these installations were global, and they’d often clash between projects. Another big issue was frequent version mismatch. I ran into cases where people were formatting the same codebase differently because they had different versions of the tools installed. Then CI would yell at everyone because it was always installing the latest version of the tools before running them. Chaos! ...

April 13, 2025

Capturing console output in Go tests

Ideally, every function that writes to the stdout probably should ask for a io.Writer and write to it instead. However, it’s common to encounter functions like this: func frobnicate() { fmt.Println("do something") } This would be easier to test if frobnicate would ask for a writer to write to. For instance: func frobnicate(w io.Writer) { fmt.Fprintln(w, "do something") } You could pass os.Stdout to frobnicate explicitly to write to the console: func main() { frobnicate(os.Stdout) } This behaves exactly the same way as the first version of frobnicate. ...

April 12, 2025

Deferred teardown closure in Go testing

While watching Mitchell Hashimoto’s excellent talk1 on Go testing, I came across this neat technique for deferring teardown to the caller. Let’s say you have a helper function in a test that needs to perform some cleanup afterward. You can’t run the teardown inside the helper itself because the test still needs the setup. For example, in the following case, the helper runs its teardown immediately: func TestFoo(t *testing.T) { helper(t) // Test logic here: resources may already be cleaned up! } func helper(t *testing.T) { t.Helper() // Setup code here. // Teardown code here. defer func() { // Clean up something. }() } When helper is called, it defers its teardown—which executes at the end of the helper function, not the test. But the test logic still depends on whatever the helper set up. So this approach doesn’t work. ...

March 28, 2025

Three flavors of sorting Go slices

There are primarily three ways of sorting slices in Go. Early on, we had the verbose but flexible method of implementing sort.Interface to sort the elements in a slice. Later, Go 1.8 introduced sort.Slice to reduce boilerplate with inline comparison functions. Most recently, Go 1.21 brought generic sorting via the slices package, which offers a concise syntax and compile-time type safety. These days, I mostly use the generic sorting syntax, but I wanted to document all three approaches for posterity. ...

March 22, 2025

Nil comparisons and Go interface

Comparing interface values in Go has caught me off guard a few times, especially with nils. Often, I’d expect a comparison to evaluate to true but got false instead. Many moons ago, Russ Cox wrote a fantastic blog post1 on interface internals that clarified my confusion. This post is a distillation of my exploration of interfaces and nil comparisons. Interface internals Roughly speaking, an interface in Go has three components: ...

March 12, 2025

Stacked middleware vs embedded delegation in Go

Middleware is usually the go-to pattern in Go HTTP servers for tweaking request behavior. Typically, you wrap your base handler with layers of middleware—one might log every request, while another intercepts specific routes like /special to serve a custom response. However, I often find the indirections introduced by this pattern a bit hard to read and debug. I recently came across the embedded delegation pattern while browsing the Gin1 repo. Here, I explore both patterns and explain why I usually start with delegation whenever I need to modify HTTP requests in my Go services. ...

March 6, 2025

Why does Go's io.Reader have such a weird signature?

I’ve always found the signature of io.Reader a bit odd: type Reader interface { Read(p []byte) (n int, err error) } Why take a byte slice and write data into it? Wouldn’t it be simpler to create the slice inside Read, load the data, and return it instead? // Hypothetical; what I *thought* it should be Read() (p []byte, err error) This felt more intuitive to me—you call Read, and it gives you a slice filled with data, no need to pass anything. ...

February 8, 2025

Go slice gotchas

Just like any other dynamically growable container structure, Go slices come with a few gotchas. I don’t always remember all the rules I need to be aware of. So this is an attempt to list some of the most common mistakes I’ve made at least once. Slices are views over arrays In Go, a slice is a lightweight wrapper around an array. Instead of storing data itself, it keeps track of three things: a pointer to an underlying array where the data is stored, the number of elements it currently holds, and the total capacity before it needs more space. The Go runtime defines it like this: ...

February 6, 2025

Function types and single-method interfaces in Go

People love single-method interfaces (SMIs) in Go. They’re simple to implement and easy to reason about. The standard library is packed with SMIs like io.Reader, io.Writer, io.Closer, io.Seeker, and more. One cool thing about SMIs is that you don’t always need to create a full-blown struct with a method to satisfy the interface. You can define a function type, attach the interface method to it, and use it right away. This approach works well when there’s no state to maintain, so the extra struct becomes unnecessary. However, I find the syntax for this a bit abstruce. So, I’m jotting down a few examples here to reference later. ...

December 22, 2024

Topological sort

I was fiddling with graphlib in the Python stdlib and found it quite nifty. It processes a Directed Acyclic Graph (DAG), where tasks (nodes) are connected by directed edges (dependencies), and returns the correct execution order. The “acyclic” part ensures no circular dependencies. Topological sorting is useful for arranging tasks so that each one follows its dependencies. It’s widely used in scheduling, build systems, dependency resolution, and database migrations. For example, consider these tasks: ...

October 13, 2024

Writing a circuit breaker in Go

Besides retries, circuit breakers1 are probably one of the most commonly employed resilience patterns in distributed systems. While writing a retry routine is pretty simple, implementing a circuit breaker needs a little bit of work. I realized that I usually just go for off-the-shelf libraries for circuit breaking and haven’t written one from scratch before. So, this is an attempt to create a sloppy one in Go. I picked Go instead of Python because I didn’t want to deal with sync-async idiosyncrasies or abstract things away under a soup of decorators. ...

October 6, 2024

Dysfunctional options pattern in Go

Ever since Rob Pike published the text on the functional options pattern1, there’s been no shortage of blogs, talks, or comments on how it improves or obfuscates configuration ergonomics. While the necessity of such a pattern is quite evident in a language that lacks default arguments in functions, more often than not, it needlessly complicates things. The situation gets worse if you have to maintain a public API where multiple configurations are controlled in this manner. ...

March 6, 2024

Strategy pattern in Go

These days, I don’t build hierarchical types through inheritance even when writing languages that support it. Type composition has replaced almost all of my use cases where I would’ve reached for inheritance before. I’ve written1 about how to escape the template pattern2 hellscape and replace that with strategy pattern3 in Python before. While by default, Go saves you from shooting yourself in the foot by disallowing inheritance, it wasn’t obvious to me how I could apply the strategy pattern to make things more composable and testable. ...

February 17, 2024

Anemic stack traces in Go

While I like Go’s approach of treating errors as values as much as the next person, it inevitably leads to a situation where there isn’t a one-size-fits-all strategy for error handling like in Python or JavaScript. The usual way of dealing with errors entails returning error values from the bottom of the call chain and then handling them at the top. But it’s not universal since there are cases where you might want to handle errors as early as possible and fail catastrophically. Yet, it’s common enough that we can use it as the base of our conversation. ...

February 10, 2024

Retry function in Go

I used reach for reflection whenever I needed a Retry function in Go. It’s fun to write, but gets messy quite quickly. Here’s a rudimentary Retry function that does the following: It takes in another function that accepts arbitrary arguments. Then tries to execute the wrapped function. If the wrapped function returns an error after execution, Retry attempts to run the underlying function n times with some backoff. The following implementation leverages the reflect module to achieve the above goals. We’re intentionally avoiding complex retry logic for brevity: ...

February 4, 2024

Type assertion vs type switches in Go

Despite moonlighting as a gopher for a while, the syntax for type assertion and type switches still trips me up every time I need to go for one of them. So, to avoid digging through the docs or crafting stodgy LLM prompts multiple times, I decided to jot this down in a gobyexample1 style for the next run. Type assertion Type assertion in Go allows you to access an interface variable’s underlying concrete type. After a successful assertion, the variable of interface type is converted to the concrete type to which it’s asserted. ...

January 31, 2024

Omitting dev dependencies in Go binaries

As of now, unlike Python or NodeJS, Go doesn’t allow you to specify your development dependencies separately from those of the application. However, I like to specify the dev dependencies explicitly for better reproducibility. While working on a new CLI tool1 for checking dead URLs in Markdown files, I came across this neat convention: you can specify dev dependencies in a tools.go file and then exclude them while building the binary using a build tag. ...

January 21, 2024

Rate limiting via Nginx

I needed to integrate rate limiting into a relatively small service that complements a monolith I was working on. My initial thought was to apply it at the application layer, as it seemed to be the simplest route. Plus, I didn’t want to muck around with load balancer configurations, and there’s no shortage of libraries that allow me to do this quickly in the app. However, this turned out to be a bad idea. In the event of a DDoS1 or thundering herd2 incident, even if the app rejects the influx of inbound requests, the app server workers still have to do a minimal amount of work. ...

January 6, 2024

Reminiscing CGI scripts

I’ve always had a thing for old-school web tech. By the time I joined the digital fray, CGI scripts were pretty much relics, but the term kept popping up in tech forums and discussions like ghosts from the past. So, I got curious, started reading about them, and wanted to see if I could reason about them from the first principles. Writing one from the ground up with nothing but Go’s standard library seemed like a good idea. ...

December 25, 2023