From JavaScript to Go: A Personal Experience
Anywhere I mention JavaScript or JS, just replace it with ES6 or above with strict mode enabled.

My experience learning German has taught me that it’s not just about learning the words and the grammar, but also learning the culture that the language represents. A word, phrase, or sentence that translate the same in two languages may mean the same thing, but would provoke very different responses. So also is learning a new programming language!
Learning a new programming language, like learning a new natural language, is by no means an easy feat. Learning the syntax and grammar is not enough to earn you a badge as a badass Java developer. You also have to learn the culture and customs of the Java developers, what they find acceptable, what they find unacceptable, their best practices, their conventions… you get the gist.
Background
I am a [maybe very] experience JavaScript developer. I’ve been doing JavaScript from the time before it was fashionable, and have come to embrace it’s flexibility as strength, instead of weakness. I’ve come to terms with the fact that JavaScript is anything you say it is: ugly, beautiful, functional, object-oriented, procedural, and declarative even. I also understand the limits of JavaScript, and the fact that forcing it into everything may be fun, but is not necessarily the best.
I am also well experienced with TypeScript. I also did a little Java when I was fooling myself with Android development, and a bit of Haskell. I also played with ActionScript back in the days. I am therefore not new to strictly typed languages.
So, I got hired by Zalando last year, and I chose to work with a team that develops back-office services and tools for Zalando Lounge. The problem: the team uses Go exclusively for everything back-end, and only use JavaScript on the front-end. As someone who has vast experience in JavaScript, I found this limiting to be confined to doing front-end work (I used to hate front-end, but that’s story for another day). Thus begins my journey int Goland (or is it Golang?).
Culture shock
GOPATH and GOROOT
The first thing that hit me about Go was how difficult it was to set up. For an experienced Go developer, this may sound ridiculous, but coming from a JavaScript world, it was super frustrating setting up the Go environment. In JavaScript, all you need is to save your code and run it. Heck, if Cmd+S is too much work for you, just copy the code and paste in your browser console or Node.js REPL. In go, I have to mark a directory as the GOPATH by creating an environment variable named $GOPATH. It doesn’t end there. My source files must be inside a directory named “src” directly inside the GOPATH.
Vendoring
Vendoring was another big shock for me. Coming from a world where “npm install xyz” [almost] always just works out of the box, getting glide to work properly was war. More on this later.
Code Style
Believe it or not, choice is not always a good thing. Sometimes you need to lose your freedom to choose to be able to see how foolish you have been all these while, arguing over things that do not actually matter. I’ve always been of the opinion that you should always use semicolon. ALWAYS. I still use semicolons in my code everyday, and use 2- or 4-space indentation, depending on my mood. I have had fights and heated arguments over the years about whether to use space or tabs or semicolons or not. Putting opening brackets on the next line drives me crazy.
When I started learning Go, I was told that those decisions have been made for me, and that they are final. There is no ESLint config to set up, no war about space or tab, no argument over semicolons. At first I found this annoying (I am human, I like staying in control), but, slowly but surely, I began to embrace the code style and have come to realise how much stress it removes from the developer to have these things decided for you. We still have fights though over whether to use “var” or use “:=”, but that doesn’t compare to the amount of disagreement we have over code style in our TypeScript and JavaScript codebase.
Similarities between JavaScript and Go
Throughout the learning process, the only thing I kept repeating was “wow, this is JavaScript”. Go is so incredibly related to JavaScript that you would think Go was heavily influenced by JS. These similarities made learning Go both fun and easy. It was almost like I was learning a new version of JavaScript (until I got to some more advanced topics).
Variables
Variables in JS and Go work almost the same. There are a few shortcuts in Go (discussed later), but otherwise if you know variables in JS, you already know variables in Go. Most of the tricks you can do with Go variables, you can also do them with JavaScript variables, even though sometimes it feels less natural in JavaScript than in Go.
Variables in Go are declared and initialized very similarly to JavaScript:
You can also do a neat variable swap in Go:
You can also easily replicate this in JavaScript using a very simple destructuring trick:
Functions
Go is a C-like, strongly typed language that is also dynamic. Like JavaScript, Go has first-class functions. This means that functions are treated like every other value. They can be assigned to variables, and the function can be called by calling the variable it’s assigned to. They can be passed as parameters to other functions, which enables the callback pattern. Go also has block-scoping, with nested blocks inheriting the scope of the parents, and the scope of a variable being where it is first declared, similar to JavaScript. However, it doesn’t hoist variable declarations like JS does (a great pain point for a lot of new JS devs).
All these combined makes Go very similar in behaviour to JavaScript. This means you can create closures in Go, as you can in JavaScript, even though they may be less useful in Go than they are in JS.
There are of course a lot of differences between JS and Go functions. Go functions, for example, can return zero or more values. You can also declare variables to be returned in Go, and then write return without explicitly specifying what to return:
In JavaScript, on the other hand, a function MUST return a value. If you don’t return any value, the function will implicitly return “undefined”. In JavaScript, we usually return an object when we need to return multiple values.
Important differences
Of course it goes without saying that quite a lot of things in JS and Go are different (if not it wouldn’t be a new language, right?). Some are slight differences, and some are very Very VERY different. I will not highlight every difference there is, but only those that could pose a gotcha risk for a JS developer.
Variable declaration and initialization
While variables in JavaScript and Go are almost the same, there are a few differences which may be significant in a few cases. The most important, perhaps, is constants. In JS, a constant means something which cannot be reassigned. In Go, however, constant means something whose value cannot be modified. A constant in Go is guaranteed to remain the same forever, while in JavaScript you are only promised that the variable will always point to the same memory location. Thus, it is impossible to assign mutable values to constants in Go. Not only that, you cannot also assign the result of a non-constant expression to a constant in go. The constant must be assigned an absolutely constant value. See code examples below.
It is also important to note that, while there is a “var” keyword in both JS and Go, both of which are used to declare variables, they are not equivalent. Go’s “var” is equivalent to JavaScript’s “let”, as JavaScript’s “var” creates function-scoped variables, rather than block-scoped variables. You can also redeclare JavaScript’s “var” in the same scope, but not “let” or Go’s “var”.
Unused variables in Go will cause a compiler error!
Values and Pointers
Pointers are probably one of the most confusing things you will meet in Go if all you have done in your life is JavaScript. In JavaScript, the rules are simple: all primitives are passed by value while every other thing is passed by reference. So, in JavaScript:
But in Go:
There is absolutely no way to force JavaScript to pass an object by value. To accomplish this, you have to use various tricks (spread is one such trick), and none of the tricks is 100% reliable. Cloning a JavaScript object using spread or other very simple workarounds, in most cases, loses vital information, like the constructor linkage and the prototype tree.
To pass an object around in Go by reference, you have to pass along the pointer to that object instead (a pointer is basically a number that represents a memory location). And don’t forget to dereference when you need the value back. This has been a great source of pain to mean as I keep forgetting each time that things are not passed by reference by default.
You can do this for every type of variable in Go. You can link any type of variable, including primitives like string.
One thing to always remember is that a pointer to an object is not equal to that object. Thus, “objA” is not equal to “objB”. They are not even the same type, so you can’t even compare them. See next section.
Numbers
There is no type called “number” in Go. Even while writing this article, I kept mistakenly writing “number” instead of “int” in the code samples, thus necessitating this section.
String literals
Simply put, Go’s strings are always double-quoted. Single-quotes is for what is called rune (char in some other languages). It takes only one character. So, always remember to quote your strings with double quote.
Equality
In JavaScript, equality is simple, as long as you use the strict equal (===). Forget that third “=” and you could be in for some nasty surprise. But getting used to the strict equal will sometimes mess up your head. I have caught myself typing “===” in Go sometimes, instead of “==”. And when I write Go code for a full day, I also have to make conscious effort to switch back to “===” when working in JS.
Two things are equal in JavaScript if:
- They are of the same type
- They are primitives and have the same value, except NaN
- They are not primitives and point to the same object
- undefined is always equal to undefined
- null is always equal to null
In Go, two things are equal if they are of the same type and have the same values. That is, if they are of the same type and “fmt.Println” prints the same output for both of them, they are equal. It is that simple. They do not need to reference the same object or the same memory location. I have caught myself a lot of times doing complicated mathematics instead of using simple equality comparison, because I forgot equality in Go does not depend on reference, but always on value.
Another important thing to remember is that you cannot compare two things that are not the same type. You can’t check if a string is equal to a number for example. You will get a compiler error. That also means that you cannot compare a pointer to a variable and the variable itself. To do this, you have to dereference the pointer first.
Goroutines
Goroutines is a powerful, yet very simple multithreading concurrency model in Go. They are like mini-threads that you can start to, for example, perform time-consuming operations (think network request) or CPU-intensive tasks, without blocking every other thing in the program. It basically allows you to run multiple functions in parallel. The Go runtime manages goroutine and spins up new threads based on need (one thread can handle thousands of goroutine). For more information see this documentation.
Coming from a JavaScript background, I was already very familiar with the JavaScript event loop, and the powerful async in JS. When you work with other languages, you understand just how convenient JavaScript’s in-built async is. It is, however, very limiting, as you cannot, for example, spin up a new thread to handle some code or something like that. The power of JavaScript’s async model ends where IO ends.
Goroutines, on the other hand, allows you to do much more, without shooting yourself in the leg like in most languages that support concurrent. Goroutines allows you to run a function in another “mini-thread” by simply prefixing the function call with “go”.
While we’re used to callbacks and promises in JS land, the standard way to return values from a goroutine is through a channel (not covered here).
It is very important to stress than goroutine are not threads, neither are they like your everyday JS async operations. One pitfall you will face is terminating programs before goroutine completes. In JavaScript, at least in Node.js, the runtime will not quit as long as there is a task waiting to be completed. So, if you have a timeout that has not yet resolved, your Node.js program will wait until it resolves. If you have a socket that is open, Node.js will wait for that socket to close. Go does not wait for goroutine to complete before terminating. As soon as the main thread does not have any other thing blocking it, it will terminate and kill the goroutine whether they have completed or not. This can be prevented by opening a channel between the host and the goroutine and waiting for that channel (waiting on the channel blocks the main thread and thus prevents termination). I am sure there are other tricks employed, but I have not tasted that water yet.
Structs vs Objects
In JavaScript you have objects everywhere. Objects in JS are so ubiquitous and so easy to create that they are sometimes they litter everywhere. They are so malleable that you can do almost anything with them. Objects are the strength and weakness of JavaScript. In JavaScript, everything is basically an object, including the primitives. Everything in JavaScript has a prototype and inherited from something else, except null and undefined. However, in this section, we will restrict the definition of a JavaScript object to anything that is not primitive.
Go’s answer to JavaScript objects is structs. However, it is important to note that they are not “objects” as you would consider objects in JavaScript. In JavaScript, you create objects in many many many ways. One of the most common ways to create objects in JavaScript is using the object literal. To create ab object of a specific type, you call the constructor with the “new” keyword.
In Go, this is similar, except there are no constructors. You can have factory functions that create and return structs, but there is no classes or constructors, thus no need for “new” keyword (though most factory function names start with “New”, which can be confusing as you would think the prefix has a special meaning).
Go “objects”” do not have methods as Go is not an Object Oriented language. Neither does Go have “this” keyword. Instead, Go associates functions with types.
Automatic semicolon insertion
JavaScript and Go both have automatic semicolon insertion (ASI). Go, however, takes ASI to a whole new level. While JavaScript will try to figure out if the next line is a continuation of the current line, Go does not even try. It simply inserts a semicolon wherever it sees line break. This can cause you a lot of headache!
If you’re used to having the opening brace on the next line, you are in for a lot of surprises.
Modules
Go modules are called packages. While in JavaScript, you define a module by creating a file, in Go you define a module by adding the “package” keyword. An important difference is that a Go package can span many files. Any file that is marked with “package users” will automatically become a part of the “users” module/package. This contrasts with JavaScript where a module is essentially a file.
Like in JavaScript, top-level variables are local to the module unless exported (including functions and types). To export a value from a JavaScript module, you use the export keyword. In Go, you export a variable by starting the name with an uppercase character. You must then be very careful with variable names in Go. I have been caught inadvertently exporting internal variables and types.
Also remember that a Go package can span multiple files. So, if you open another file that is also in the “users” package, you will be able to also access all top-level variables, including those that were not exported.
Error Handling
See below.
What I like about Go
Go is an awesome programming language. It is easy to learn, if you already know JavaScript very well, as they have a lot of similarities and some concepts won’t be new to you.
The first thing I liked about Go is the simplicity. Go feels like a dynamic language, when it actually is actually compiled. Though it has a runtime, it is also not an interpreted language, unlike Java and JavaScript, and does not depend on any framework, unlike .NET. It really is like getting the best of both worlds.
I also like that garbage collection is inbuilt in Go. It does not mandate you to clean up by yourself, unlike C. However, this can cause some trouble sometimes due to long GC pauses, but these rarely happen unless something is really bad. This also means that you are not 100% in control of the memory usage, which may be disadvantageous in embedded systems where memory is a big deal.
Go is fast! It is not surprising, seeing it is compiled to machine code. However, it is still significantly slower than C and C++, and probably comparable to Java. I don’t know, all I know is that it’s noticeably faster than Node.js. Go also uses much less memory.
Go is also very flexible. Unlike Java for example. It allows you to do certain things in Javascipty way, without the headache. It’s so flexible that you forget it’s a compiled language sometimes.
Most importantly, I like Go’s debugger. Go has one of the most powerful debuggers I’ve worked with. It is so easy to use that I was already debugging one month in.
Another area that Go blows JavaScript away is it’s standard library. Almost anything you need to write a fully functional program is included in the Go standard library. Take that JavaScript!
One of my favourite features of Go is that you can compile it for any architecture, from any architecture. For most languages, you have to switch to a MacOS to compile for MacOS, and to ARM to compile for ARM. For Go, you have to just pass a compile-time flag to compile for any given architecture.
What I wish I can change
Error handling
Error handling in Go feels like the early days of Node.js, where there is no error if the first param to the callback is null. In Go, the error is usually the last param. There is no way to throw an exception in Go, you simply have to pass the error manually from function to function. This can get tedious, as you check if there is an error after every operation that can produce an error (which is more than half of all operations from experience). This litters the whole code with “if err != nil”.
Though Go has an error type, it’s really not anything special. It is not treated specially like in Java for example where you can catch an error. Even panic receives a string value, not an error.
Stack tracing is not the best I’ve seen. Since errors don’t get thrown, but are simply returned, it is difficult to trace where an error occurred after it is received. The stack trace contains where the error variable is defined, not where it is “thrown”.
Third-party libraries
You can install a Node.js module to achieve almost anything by randomly trying “npm install do-anything”. Almost everything you want to do has an npm library or two. Or seven. Third-party libraries in Go are so unavailable that we have to write our own libraries sometimes. This is however mitigated by the fact that the standard library is much more complete. Still sucks though.
Vendoring
Not everyone would agree, but Go’s package dependency management is dwarfed by npm. There are a lot of issues with npm, but ease of use is definitely not one of them. Installing a Go dependency does not automatically install the dependencies of that dependency. So you have to manually install everything your dependency depends on. Luckily, Go packages don’t tend to depend on a lot of libraries. I can’t say the same for NPM packages.
If a dependency uses a package that you (or another dependency) also depend on, but requires a different version, you might be in a very very deep trouble, as there is no way that I know to resolve this (maybe I am wrong). In Node, if a package requires a different version of a module, a local copy is installed for that specific package. This is not the same in Go.
Project setup
I hate being told where to put my files and the file structure I must follow. I hated this about Java, but Go has it worse. I wish I can have my Go file anywhere I want and it will intelligently know where is the project root and work with it.
Summary
Go is a sweet sweet language. It is powerful, and it close enough to JavaScript to be much easier to understand for someone with a JavaScript background. If you are a JavaScript developer, and are interested in learning a new language to increase your scope, I would strongly advise you consider Go. It does have a few annoyances, but JavaScript probably has more. It makes up for most of its annoyances by being simple to use, and flexible. I still have a long way to go before I can be half as good in Go as I am in JavaScript, but with constant practice I will get there. Tschüss.