“Sorry, I’m just go-zoned out…”

Coming to terms with thinking in Go.

I’ve been writing code for about 12 years now — I started off with Visual Basic(don’t judge me, it was brilliant for its time), moved on to VB.NET, then C#, then Java. By the time I dropped out of college in 2010, I was fairly proficient with these languages, and I’d started diving into Ruby and Python. Soon, Javascript came calling, as did F# and Julia.

It’s been so long writing code in dynamic, interpreted languages that it’s very easy for me to take them for granted. I need to call an API endpoint, parse its response and call another API? No problem. Process a hundred thousand rows from MySQL and push them into a Google Cloud Storage bucket? Roger that. In a nutshell, I didn’t have to think about how I’d go about doing those tasks, whether I knew what kind of variables and/or data I’d be working with. I didn’t have to worry about parsing JSON into a Python dictionary, or a set of MySQL rows into tuples — never had to think twice, or even once, about the underlying structures. It would happen automatically. And I thought that was awesome.

Then I decided to learn Go.


Go seemed like a great choice for my next language. Great performance, static typing, compile-to-binary, a great community — sounds like a great set. A couple of things didn’t seem so great, when compared with Python — for instance, installing third party package management with Go isn’t as intuitive as pip is for Python. But those are things people are hard at work solving, and I figured it was high time to take the plunge.

I decided to create a Go clone for the backend API that powers the product I’m building along with Kshitish Purohit— it would be a great way to evaluate Go’s viability as a primary tool, and it could even lead to a better backend for us to deploy. I feel building a real-world codebase is the best way to get a feel for a language and its idiosyncracies. Read up the first few pages of a book or a tutorial, and then just dive in.

Now, this post tries to lay out the various “issues” I ran into, while testing the waters of Goland. When I ran into them, it seemed like an issue with the language; a day later, it was pretty obvious the issue was how I approached thinking about code, not the language. That’s what the essence of this post is about.

In the examples below, we’ll be playing with the Random User Generator API — do check it out, it’s a great way to generate random data for development.


One of the first things I noticed was how the whole concept of modules works. In languages like Python, I could define a class in one module, and then explicitly import it in another:

It’s slightly different in Go:

I’ve defined a struct in user.go, and created an instance in app.go. As long as they’re part of the same package, Go shares every object you create across all your code, without the need for explicit imports.

That can take some getting used to, especially if Go’s package constructs aren’t too familiar.


Structs are your BFFs.

Please read that once more.

Read it twice? Now read it one last time.

In my (woefully limited) experience with the language, structs are very, very essential. If you’re parsing incoming JSON, you need to parse it into a struct. If you’re querying MySQL, you need to parse those rows into structs.

Remember the Random User Generator? Let’s see how we might go about defining a struct for its response. If you hit http://api.randomuser.me/0.6/?nat=fr&results=10, this is the sample response:

{
"results": [
{
"user": {
"gender": "female",
"name": {
"title": "mlle",
"first": "hanaé",
"last": "caron"
},
"location": {
"street": "5215 quai chauveau",
"city": "montpellier",
"state": "eure-et-loir",
"zip": "10442"
},
"email": "hanaé.caron64@example.com",
"username": "bigpeacock558",
"password": "garcia",
"salt": "HZnWtlJo",
"md5": "80b8eedb8c8819765268c48cc5f14369",
"sha1": "2abc7a619c60a2f0b4f7d92f1f77ec4e022598f3",
"sha256": "573d6ddad069b78b7f7476d815418549f607e7a631c01d26d722829647a6cd62",
"registered": "1094007511",
"dob": "66115724",
"phone": "02-84-57-35-18",
"cell": "06-32-51-27-04",
"INSEE": "2720186060152 31",
"picture": {
"large": "http://api.randomuser.me/portraits/women/30.jpg",
"medium": "http://api.randomuser.me/portraits/med/women/5.jpg",
"thumbnail": "http://api.randomuser.me/portraits/thumb/women/71.jpg"
},
"version": "0.6",
"nationality": "FR"
},
"seed": "92fe0594f57cfc36"
},
{
"user": {
"gender": "female",
"name": {
"title": "mme",
"first": "pauline",
"last": "david"
},
"location": {
"street": "3602 rue des jardins",
"city": "saint-pierre",
"state": "oise",
"zip": "53239"
},
"email": "pauline.david33@example.com",
"username": "redmeercat367",
"password": "2626",
"salt": "wRpkiFYh",
"md5": "44ffad43d6d317f75081860ed46dffbf",
"sha1": "937e868c5a6bb481b9b52dba11f84dc7305150b1",
"sha256": "d68865ce85d541b5c36367810fec45c4d115e3715071c2bbc065787b8cee62cf",
"registered": "1213238587",
"dob": "54433078",
"phone": "03-09-54-83-36",
"cell": "06-25-22-10-71",
"INSEE": "2710826105268 07",
"picture": {
"large": "http://api.randomuser.me/portraits/women/89.jpg",
"medium": "http://api.randomuser.me/portraits/med/women/78.jpg",
"thumbnail": "http://api.randomuser.me/portraits/thumb/women/38.jpg"
},
"version": "0.6",
"nationality": "FR"
}
},
{
"user": {
"gender": "male",
"name": {
"title": "m",
"first": "livio",
"last": "francois"
},
"location": {
"street": "2147 rue de l'abbé-gillet",
"city": "limoges",
"state": "pyrénées-orientales",
"zip": "35257"
},
"email": "livio.francois47@example.com",
"username": "organickoala533",
"password": "celebrity",
"salt": "MkDR2bAj",
"md5": "ed2f0cb8b668615fcd1a03f11fb76c8b",
"sha1": "d8864301672da0e322554bfffe2a09ee6db12c30",
"sha256": "42ee5bab7e1ed03305285bc208caffdbf14d801d6922ac7525034df00d26c646",
"registered": "1424516041",
"dob": "218726222",
"phone": "02-93-46-07-80",
"cell": "06-12-62-46-74",
"INSEE": "1761125229017 94",
"picture": {
"large": "http://api.randomuser.me/portraits/men/99.jpg",
"medium": "http://api.randomuser.me/portraits/med/men/34.jpg",
"thumbnail": "http://api.randomuser.me/portraits/thumb/men/64.jpg"
},
"version": "0.6",
"nationality": "FR"
}
},
{
"user": {
"gender": "female",
"name": {
"title": "mme",
"first": "flavie",
"last": "lemaire"
},
"location": {
"street": "2515 avenue paul eluard",
"city": "dunkerque",
"state": "haute-saône",
"zip": "98808"
},
"email": "flavie.lemaire11@example.com",
"username": "bluedog680",
"password": "picture",
"salt": "4JYQ6Q1c",
"md5": "b4b0be1a456d22fed61f11086bcc4107",
"sha1": "e2ff327a3b56e73d64c12a20363424eb1806cf68",
"sha256": "777b7e921c900e9a981b4b7984ca1a7cc079f1378a9318ca8a71c93ffc551de4",
"registered": "1479955860",
"dob": "221497549",
"phone": "03-77-53-50-17",
"cell": "06-79-89-44-85",
"INSEE": "2770069935366 72",
"picture": {
"large": "http://api.randomuser.me/portraits/women/74.jpg",
"medium": "http://api.randomuser.me/portraits/med/women/3.jpg",
"thumbnail": "http://api.randomuser.me/portraits/thumb/women/55.jpg"
},
"version": "0.6",
"nationality": "FR"
}
},
{
"user": {
"gender": "male",
"name": {
"title": "m",
"first": "benjamin",
"last": "masson"
},
"location": {
"street": "6752 rue louis-garrand",
"city": "paris",
"state": "haute-corse",
"zip": "79481"
},
"email": "benjamin.masson34@example.com",
"username": "heavyswan205",
"password": "alaska",
"salt": "Bxk76bHG",
"md5": "62beaa5cb6da59e365b3219d8f7180c5",
"sha1": "03b206df9411ef2249a7f59875d560ffe8161bc2",
"sha256": "a1391024c9a8ec58c224ccf46a9d97a37858a223e845c57e33a2ba21c12fcd7b",
"registered": "1354709819",
"dob": "164117946",
"phone": "04-36-36-76-94",
"cell": "06-83-19-65-80",
"INSEE": "1750282868053 34",
"picture": {
"large": "http://api.randomuser.me/portraits/men/97.jpg",
"medium": "http://api.randomuser.me/portraits/med/men/2.jpg",
"thumbnail": "http://api.randomuser.me/portraits/thumb/men/88.jpg"
},
"version": "0.6",
"nationality": "FR"
}
},
{
"user": {
"gender": "male",
"name": {
"title": "m",
"first": "alban",
"last": "roussel"
},
"location": {
"street": "2853 avenue tony-garnier",
"city": "brest",
"state": "ariège",
"zip": "44648"
},
"email": "alban.roussel81@example.com",
"username": "brownduck595",
"password": "chicks",
"salt": "5MYxg2PW",
"md5": "e172294a0590e057cf5cda571f9ebf02",
"sha1": "08136ad9f9bbdbbb47c7d854cab9ea350dd34ede",
"sha256": "490f05d0b4d07bf1f6919451ec3ed66aaf2cb588955331a26a2aab22f5cab963",
"registered": "1356793325",
"dob": "443377272",
"phone": "01-10-68-30-55",
"cell": "06-23-48-71-21",
"INSEE": "1840042808098 57",
"picture": {
"large": "http://api.randomuser.me/portraits/men/32.jpg",
"medium": "http://api.randomuser.me/portraits/med/men/74.jpg",
"thumbnail": "http://api.randomuser.me/portraits/thumb/men/31.jpg"
},
"version": "0.6",
"nationality": "FR"
}
},
{
"user": {
"gender": "male",
"name": {
"title": "m",
"first": "elio",
"last": "berger"
},
"location": {
"street": "2340 rue de cuire",
"city": "angers",
"state": "lozère",
"zip": "18470"
},
"email": "elio.berger47@example.com",
"username": "crazyduck627",
"password": "brett",
"salt": "wFhaCERv",
"md5": "c613e61ce7ab9deb3179f310570fb124",
"sha1": "c57ab148648204cce2a22ed14118519c0fec4656",
"sha256": "212038a564ad4e33296858198b9d2ba26df8e802e4177d1d2966e60756c0f315",
"registered": "1369840688",
"dob": "166320448",
"phone": "03-73-25-47-60",
"cell": "06-42-27-04-05",
"INSEE": "1750388491392 50",
"picture": {
"large": "http://api.randomuser.me/portraits/men/63.jpg",
"medium": "http://api.randomuser.me/portraits/med/men/15.jpg",
"thumbnail": "http://api.randomuser.me/portraits/thumb/men/22.jpg"
},
"version": "0.6",
"nationality": "FR"
}
},
{
"user": {
"gender": "female",
"name": {
"title": "mlle",
"first": "enora",
"last": "leclercq"
},
"location": {
"street": "7681 place de l'abbé-georges-hénocque",
"city": "le mans",
"state": "corse-du-sud",
"zip": "49587"
},
"email": "enora.leclercq85@example.com",
"username": "lazygorilla736",
"password": "second",
"salt": "MEYXfrla",
"md5": "7b25fd7fe36ee36a2285531befd325a1",
"sha1": "2c63fa5f990f0e6cd54c8d36cf54e47973bfe122",
"sha256": "6f9d766ebab64a5ee8a70d031227b2c916515f03a58d162e059ceeff8c2e84ee",
"registered": "1053709071",
"dob": "418669881",
"phone": "04-23-74-22-70",
"cell": "06-20-29-02-15",
"INSEE": "2830337196598 43",
"picture": {
"large": "http://api.randomuser.me/portraits/women/65.jpg",
"medium": "http://api.randomuser.me/portraits/med/women/12.jpg",
"thumbnail": "http://api.randomuser.me/portraits/thumb/women/95.jpg"
},
"version": "0.6",
"nationality": "FR"
}
},
{
"user": {
"gender": "female",
"name": {
"title": "mlle",
"first": "mya",
"last": "aubert"
},
"location": {
"street": "1199 avenue paul eluard",
"city": "boulogne-billancourt",
"state": "loire",
"zip": "35297"
},
"email": "mya.aubert47@example.com",
"username": "blackgoose803",
"password": "godzilla",
"salt": "C44avpgv",
"md5": "169007ff74e62e1e905d9fa85ca34f4a",
"sha1": "5e7188d85b899c22dda5505c8a64deb4b6db52c1",
"sha256": "5189ac15744be79706452461440c28bc56c430396c25e03fd92286e7348ce123",
"registered": "1362089904",
"dob": "285790186",
"phone": "01-72-74-51-19",
"cell": "06-03-93-84-47",
"INSEE": "2790001960618 91",
"picture": {
"large": "http://api.randomuser.me/portraits/women/74.jpg",
"medium": "http://api.randomuser.me/portraits/med/women/57.jpg",
"thumbnail": "http://api.randomuser.me/portraits/thumb/women/47.jpg"
},
"version": "0.6",
"nationality": "FR"
}
},
{
"user": {
"gender": "male",
"name": {
"title": "m",
"first": "romain",
"last": "moulin"
},
"location": {
"street": "7741 rue des cuirassiers",
"city": "toulon",
"state": "vienne",
"zip": "90114"
},
"email": "romain.moulin66@example.com",
"username": "redbird943",
"password": "diao",
"salt": "WANn6YRS",
"md5": "e1956e00a3e2258838a6c5e9fb412bcf",
"sha1": "f6cc8210efc8b563708e60ce2754028c4bb654bb",
"sha256": "0cbdaddfb6cd4600f526d1eae91261f37083e78a06304953ba9079e58b2b64dd",
"registered": "1309651252",
"dob": "370556957",
"phone": "03-22-72-46-17",
"cell": "06-58-68-63-88",
"INSEE": "1810867807927 90",
"picture": {
"large": "http://api.randomuser.me/portraits/men/76.jpg",
"medium": "http://api.randomuser.me/portraits/med/men/84.jpg",
"thumbnail": "http://api.randomuser.me/portraits/thumb/men/71.jpg"
},
"version": "0.6",
"nationality": "FR"
}
}
]
}

The first thing we need to do, is create a struct type that corresponds to this JSON payload’s structure. And believe me, it sounds like a lot of work. It IS a lot of work, going through the payload, figuring out how to map nested structures and so on. But the best part — you don’t have to do it manually. There are some brilliant converters online that spit out struct definitions for any JSON payload. I personally use JSON-to-Go.

Anyway, here’s the struct definition for the payload we just received:

Brilliant. That did seem like a lot of work at first, but JSON-to-Go took care of that. Notice how each struct field has a json property to it — those are the field’s corresponding key names included in the JSON payload; Go uses these properties to determine how key values are mapped onto the struct’s fields.

Okay, so now I really want to call this API from my Go code. Personally, I think Kenneth Reitz has set the bar pretty damn high when it comes to HTTP requests libraries — lucky for us, another Gopher feels the same.

Let’s grab the library first:

go get -u github.com/levigross/grequests

….and now…

We’ve made sure RandomUser.go is a part of the same package as RandomUserStruct.go, in the same directory. We’re importing a couple of libraries — fmt has printing functions, encoding/json does what its name suggests, and grequests is a great HTTP requests library for Go.

Then comes the main function — getRandomUser(). First, we go ahead and create a struct instance called random_users; then we hail the RandomUser.me API to give us some random data. Once that’s done, we use the json package’s Unmarshall function, to unpack the response’s bytes into the struct instance we defined earlier.

To be fair, I could do this with maybe 2–3 lines of Python:

Okay, I actually did it in just two lines. This made me feel pretty confused, about Go — if typical tasks take up so much code, what’s the point?

It came to me later — for the first time in ages, I’d actually sat down and thought about the task at hand, how my code would be structured, what kind of data I’d be working with. Sure, it took a bit of extra effort, but when I went ahead and wrote the little piece calling that API, I knew what I’d be dealing with.

Maybe that’s the biggest value prop about static types in general, and Go in particular — if you spend some time figuring out how you’re going to deal with various types, your code will be so much more safer, without weird bugs creeping in. Honestly, that does happen with Python or Javascript a lot — and if Go can help alleviate that, that’s pretty cool.


I don’t know about other people who’ve approached Go from dynamic languages, but I personally think it requires being very open to changing how you see your code deal with different scenarios. And believe me, it wasn’t easy at first; I was all set to drop Go and stick to Python. But sleeping over it did help, and now I have a better sense of appreciation for Go. I’m sure there will be more incidents popping up, making me wonder why the simplest of tasks can take longer with Go. I’m certain, though, that I’d be able to move around those — and I’m even more certain that Go will make me a better programmer, once thinking in types and structs becomes second-nature to me.

If you’re at a point where you’re wondering whether Go’s worth investing your time and energy in, please don’t think any further — go for it. Chances are you won’t regret it.


If you liked this post, allow me to shamelessly ask you to click that little heart below this post :)