Why I miss generics in Go

I’ve programmed in a number of different languages over the last 15+ years, but can confidently say that I have most enjoyed the experience with Go over the last 2 or so.

Rather than talk at length about all the things I like (there are many), I wanted to highlight a problem we were trying to solve at work which made me yearn for generics.

We were writing a series of microservices, to be run in docker containers, to provide REST interfaces to a bunch of different resources. The details of these resources would be stored in CouchDB and parts of the services would react when objects were created, updated, deleted etc. In the end, it took 3 attempts to get at the (not entirely satisfactory) solution we have today.

Attempt no. 1

Our first approach was to just write standard code for each service. We wrote one service first with handlers for all of the common REST endpoints. This worked OK for one service, but then we found ourselves copying, pasting and editing the code for each new service. Fixing a bug in one meant manually fixing that in each of the others. This soon became very inefficient.

Attempt no. 2

To try to reduce the code duplication, we wrote a library which handled moving objects into and out of couchdb and responding to the REST endpoints. To support the different types of objects each service needed to use, these functions had interface{} everywhere leading to large amounts of casting required. It also meant that we’d lost a lot of the compiler checking we’d got used to.

To allow us to manage the data that was returned as distinct to what was stored in couchdb, we’d have both an HTTPObject and a CouchObject and would convert between the two as appropriate. Using this generic interface{} approach we had numerous issues with converting between the two objects and also developers writing a call back (for example a PostCreate handler) and expecting a different type of object to the one they received.

Attempt no. 3

Attempt number 3 gave us the library we’re using today. We used Matt Ryer’s genny library to generate the REST handler code for each of the different types we need to support. This has solved a number of the problems in the previous attempts including:

  • A single place to update code for bug fixes / feature changes
  • Compiler support for different types
  • Unambiguous types used for callbacks
  • We can very quickly add a RESTful api backend for a golang struct with little new code

However, there are still a number of disadvantages with this approach:

  • We have to explain what genny is, what it’s used for and how to new people that join the team. This contradicts what we’ve tried to do with the majority of our code in staying close to the Go stdlib to reduce the amount of specialist knowledge people need.
  • If we check-in the generated code, we have to explain to new team members not to change it where they see it.
  • If we don’t check it in, then we have to explain to them how to generate code before they can run the service
  • It adds a large amount of clutter to each of the projects in the IDE when all of the functionality should (and in some ways is) held elsewhere in a library

Summary

For me, the problem we were trying to solve here is a perfect candidate for generics. If I’d been trying to solve the above problem in Java, I would have used generics as a starting point. I haven’t been able to find a way that encapsulation would solve the problem here. Having generics as a first-class part of the language would make this clearer and more elegant.

Like what you read? Give Andrew Stock a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.