Golang tips and tricks, part 3

When you’re writing software it’s not so much the one liner that changes how you work, but the balancing act of programming patterns you use to build your software. I’m hoping to outline a few rules and patterns which make sense to me when building out software in Go.

Passing along data and objects

As you know by this series of articles, I’m very much interested in how to pass stuff to where you will needed. One of the most common patterns with API development that I’m dealing with is writing handlers like these:

func getJob(w http.ResponseWriter, r *http.Request) { ...

The obvious caveat of writing handlers like this is thinking about how and where you will get your data from. Or how you will access a database client. Or some other object that is specific to you and your main package.

What I do, is I provide a function which returns the handler with the above declaration. The function itself can take any number of arguments that you see fit - and you usually pass things from your main function.

func EndpointRun(hub *Hub) func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) { // your code goes here, you can use `hub` :) } }

The endpoint can be used like this:

m.HandleFunc("/api/{token}", EndpointRun(hub))

One benefit of this method is that the EndpointRun function can be part of another package. All you have to do is make sure that this package knows about what Hub is. It’s a good idea because of this to either keep everything in the main package, or to use packages but declare all structures in a separate package so they can be reused.

Passing along functions

The EndpointRun example above has the pitfall that you’re just wrapping your code, which might always need *Hub for every call that you implement. If you consistently use the same pattern and have a significant number of API calls you’re implementing, you could use something like this:

type HttpHandler func(http.ResponseWriter, *http.Request) type MyHandler func(*Hub, http.ResponseWriter, *http.Request) func httpHandler(hub *Hub, callback MyHandler) HttpHandler { return func(w http.ResponseWriter, r *http.Request) { callback(hub, w, r) } }

I’m using types here just to shorten the declaration of httpHandler a bit, trying to keep line length a bit shorter. As you see, I declared MyHandler function with the additional hub parameter which I want to pass.

m.HandleFunc("/echo", httpHandler(hub, echo))

There is only some benefit to this madness - when you will decide to add or remove a function parameter to your API declaration, you would modify httpHandler (or MyHandler type, respectively). When you do this, you can rely on go that it will error out when building/running, until you will add the parameter to all the API functions.

Ahh - the benefit of a strongly typed language. I could be hunting down errors like this for a very long time - Go is reasonably safe for refactoring in the sense that most likely you will break your development build long before an issue due to such refactoring would crop up in production.

Casting

You may come across a scenario where you need to add some functionality around your data structures. For example - you might be building a chat application where you want all the data inside a PayloadClient structure, and you also want to have a Client object which implements additional interfaces or properties. For example:

type Client struct { PayloadClient send chan interface{} hub *Hub conn *websocket.Conn }

An embedded structures fields may be used from the Client object, so you could use c.Nickname in this case, but you can also use PayloadClient in the same way:

func (r *Client) ToPayloadClient() *PayloadClient { cl := &r.PayloadClient return cl }

So, we’re actually taking casting out of the equation here. We’re literally using the underlying data type to provide the value which is required.

Why would you declare two structures like this? Separation of responsibility.

The declaration of PayloadClient is dealing exclusively with data structures which are used for different types of storage - for example, a database table, or a JSON string. It makes sense to provide functions which work on this object that deal with storage.

And on the other side, the declaration of Client is dealing with functionality. There’s a send channel which is used for transmission of message payloads, there’s the actual websocket connection used by a client, and there’s the hub - a registry of all clients. None of these things translate into anything that’s visible in the storage layer and I’m not necessarily of the opinion that all this should be piled onto one object.

It’s very difficult to strike a good balance between the two sides.

Using packages

If you’re just using the main package and don’t use additional packages, you should be fine. But if you’re using packages, there’s at least one rule of wisdom which I can pass forward:

Declare your structures in a package which you can import!

Well, the wisdom explains what the problem is. Let me just explain how you’d get to the problem. When writing APIs, I like to use packages to organise my code. I start out with an empty main , and I declare some esoteric dependencies in the common package, and more concrete dependencies, like a database client in the factory package. All of this works well exactly for one reason: I don’t need any structures in common or factory packages.

Then, when writing a web application, there’s usually three things you need to worry about. API calls, front-end calls, and underlying data storage. I want to suggest to create the api , web and structs packages for this scenario.

The structs package must not import the api or web packages. If you’re doing this, you already fucked up and should just move all your code into main or start reading this section from the beginning.

The structs package must not have any internal dependency, which may again have the structs package for a dependecy. You’ll make the Go compiler angry with a circular dependency.

The web package may import the api package and use things already declared there. I’m pretty sure that most if not all things that are available on the web can be created as APIs. Or atleast they can be declared in the api package, even if they are never exposed via a HTTP interface.

That’s the logic. The problem always begins, however, that you start with main . As soon as you declare a structure in main, even if it’s for something simple like configuration flags - you have to make damn sure that this structure is accessible in your packages. And it seems like a bad reason to resort to interface{} .

As for the code, I can’t point to anything of mine which uses these patterns (yet), but I did stumble across the “adopt a source” project, which seems to use similar patterns. You can check out adopt a source on github as an example of a project laid out over several packages.

The argument for reusability

Most of the problems we create for ourselves. When arguing code reusability against the alternative, starting off with a sane boiler plate, you just have to figure out when the cost will bite you in the ass.

One of my first CMS projects started without code reusability. The first 10 pages we made on them suffered from evolution - it was obvious that things were improving from one to the other, and it was inevitable that we will have to go back and fix it - at least the administration panel. And we did. And we created about 150 more web pages at an astounding pace. And it was still mostly copy-paste (files).

Of course, the next step was to build out a core , which I’m happy to say constantly improves, is unit tested, and runs a ridiculously significant number of projects. And the core gets updated with new features, and old features either get phased out, or refactored keeping the existing interfaces for backwards compatibility.

So, over all, reusability is just like any other approach in programming. You have to know where it can be used to achieve what is most important to you. If you’re building out just a testing web service, you’ll most likely not need to pay much attention to it. But when you’ll have 5 of them, patterns will emerge.

Closing words

The article is part of “Diving deeper with Go” series where I try to discover and present knowledge about how to use Go effectively. You might want to read the other parts:

While I have you here...

It would be great if you buy one of my books:

I promise you'll learn a lot more if you buy one. Buying a copy supports me writing more about similar topics. Say thank you and buy my books.

Feel free to send me an email if you want to book my time for consultancy/freelance services. I'm great at APIs, Go, Docker, VueJS and scaling services, among many other things.