When I came from the very dynamic world of ruby, my first approach to designing HTTP clients in go was to think about mocking similar to what I was used to in Ruby. I quickly learned that it’s not the ideal approach in Go, and over time have learned a few tips and tricks for dealing with HTTP clients.
Let’s imagine we have a hypothetical Runtime
client with just one public method:
If the implementation is mostly executing an HTTP request against a URL, one can imagine the following simple implementation:
This all makes sense so far, and…
When I started picking up the go language years ago, things that stood out to me immediately were the io
and net/http
packages. Both of those packages were extremely well done and minimal in the amount of interfaces they exposed to the user.
The two things which stand out for me are:
The primary constructs you’ll interact with are pretty limited:
To build your service:
http.Handler
http.HandlerFunc
http.Server
To handle requests:
http.ResponseWriter
http.Request
To build a client:
http.Client
http.Transport
In total, we’re looking at 7 core primitives to build a client and server — which isn’t a…
When I mention dependency injection to folks who have Java or .NET backgrounds it usually invokes a few sets of questions — to which my usual response is:
It’s likely not as sophisticated as what you’re thinking — and it doesn’t have to be!
While there are methods to use more sophisticated approaches in Go, starting off projects the simplest way possible is still the preferred approach.
Dependency injection represents the D in SOLID principles. Quoting from wikipedia:
In object-oriented computer programming, SOLID is a mnemonic acronym for five design principles intended to make software designs more understandable, flexible, and…
One of the most prevalent patterns when managing the lifecycle of goroutines in Go is to have a done
channel. Here’s a quick example showing this in action:
done := make(chan struct{})for {
select {
case <-done:
return
case job := <-jobs:
// execute job
}
}
In fact, this is pretty much how the Done
channel in context is done under the hood.
If we take a look at how it’s done in the context package, it simply uses a mutex to ensure that the action of closing the channel is only done once.
Here’s the relevant snippet…