Go is fast, powerful, and "beautiful," developer says.

Todd Kuehnl has been a developer for nearly 20 years and says he's tried "pretty much every language under the sun."

But it was only recently that Kuehnl discovered Go, a programming language unveiled by Google almost four years ago. Go is still a new kid on the block, but for Kuehnl, the conversion was quick. Now he says "Go is definitely by far my favorite programming language to work in." Kuehnl admitted he is "kind of a fanboy."

I'm no expert in programming, but I talked to Kuehnl because I was curious what might draw experienced coders to switch from proven languages to a brand new one (albeit one co-invented by the famous Ken Thompson, creator of Unix and the B programming language). Google itself runs some of its back-end systems on Go, no surprise for a company that designs its own servers and much of the software (right down to the operating systems) that its employees use. But why would non-Google engineers go with Go?

This is just a picture, but you can try Go within the browser at golang.org.

Kuehnl is the head of engineering for Beachfront Media, which recently announced a new platform called Beachfront.iO that serves video ads to mobile devices and tablets. Kuehnl spent three months leading development of the platform, saying he chose Go in large part because of how it enables concurrency, the ability for computations to execute simultaneously and interact with each other while they're running.

Beachfront.iO "needs to service millions upon millions of requests a day, upwards of 5 to 10 thousand transactions per second and do this very reliably," Kuehnl said. "I was looking at different stacks and doing benchmarks to figure out how it would be most productive and most performant … and that's where I came across Go."

The code written in Go performs all the heavy lifting on the back-end, including load balancing and choosing which ads to serve up when and where.

"The issue for PHP and even Node.js is obviously you're trapped in a single-threaded situation and what I really wanted was to be able to do a lot of things concurrently," Kuehnl continued. "My options were to go with something like Java, where you have more memory overhead, or I could go with something like Go that was built from the ground up for concurrency and using very modern patterns."

Kuehnl said Go combines those modern concurrency patterns with the "static execution speed of C or C++" but with "a more compositional feel, a script kind of feel. … I started first with the idea of trying to pick the most high-performance modern language, but as I explored it more the beauty of the language presented itself."

Beachfront runs its video ad service on Ubuntu Linux servers hosted in the Amazon Elastic Compute Cloud. Go performs well even on cloud-based virtual machines (albeit large instances with 16GB of RAM), Kuehnl said. This allows Beachfront to minimize one of the pain points of advertising—lengthy delays that cause users to give up and move to a different website or app. "With Go, I'm able to have an ad up and running within 200 milliseconds, which is orders of magnitude faster than some of these legacy networks," he said. Go's compiler is so fast "it's almost like working in an interpreted language," he said.

He's not the only one

Google unveiled version 1.1 of Go earlier this month, promising a big speed boost. (Kuehnl said he's already using the new version.) Google, which is also developing a JavaScript alternative called Dart, declined an interview request from Ars about the future of Go. Google noted, however, that Go is used internally by the company for "core infrastructure services; cluster management; large-scale distributed processing, including some uses of MapReduce; [and in] tools for software development."

At least one project using Go did end in failure. That was a Kickstarter-funded game called "Haunts: The Manse Macabre." The developer blamed much of the trouble on Go itself, but his descriptions seem to indicate the problems were mostly self-inflicted through poor planning and version control. The developer cited "the shifting code base of the Go programming language" and the fact that the language is "new and not well supported." But he also noted that the code his team developed is "buggy and incomplete" and that the original programmer on the project took on a new job and "has not responded to additional requests for aid or insight into solving" the fundamental problem that the game code can't be compiled.

Go has worked out well for numerous other users. A developer named Dotan Nahum recently used Go to build a remote management system for the Raspberry Pi called Ground Control.

Nahum told me via e-mail that he chose Go for its "performance, concurrency, and simplicity." Ground Control required a Web server and monitoring daemon, and his options "were to either go with C and write a pure C-based solution, or use something I knew was the closest thing to C in concept and offered great performance (in its recent version): Go."

Nahum wasn't sure how Go would work on the Pi's ARM architecture, but it turned out the Pi "took everything I threw at it without any problems."

Nahum further stated:

Being polyglot for a long time now, I use the right tool for the job. Instead of being stuck fitting square pegs into round holes (which sadly many programmers do), I keep exploring more and more platforms and languages in order to gain high expertise in those and understand what situations call for the right platform and language.

In my opinion Go gives you great performance—which is easy to verify yourself and is more or less a fact; but what's more arguable is that I think it gives you great concurrency. More precisely I think it gives you great concurrency, and simple concurrency, for the amount of effort you put in.

I see Go as simple in general, too. It doesn't try claim it is a superstar like other languages have in the past, it has simple language constructs (in my opinion), and the ecosystem is very humane—from the documentation to the developers' general approach. I think you can attribute those properties to languages like Ruby and Clojure too—I see Go as a distant relative.

Go still has some growing up to do, though—no surprise given how new it is. Nahum said alternatives like Ruby and Node.js have more complete ecosystems and that Go's packaging and dependencies could stand some improvement. "If you make a few searches, you'll see some confusion there and several alternate solutions for what exists right now to specify your dependencies," Nahum wrote. "Some say that it's just a matter of people not getting it right, and some say that it shouldn't work the way it does right now. I'm not so fond of the way it works right now, but I can also make do for now—it could have been much worse (I don't want to say which language I'm talking about :)."

Kuehnl noted that Go is probably not yet the best choice for creating desktop apps with graphical user interfaces, or for gaming. "I think Go fits best for what it's really being used for in Google, which is back-end services, back-end processes, data analysis, things like that," he said.

Kuehnl watched some video lectures and read documentation about Go when he was learning the language, and said it didn't take him to figure out how best to use it.

"There's sort of a C kind of feel to it but it's definitely different, it's got some influences from Haskell and some other languages in regard to some of the concurrency aspects," he said. "I found Go to be very natural really within a few days. What I love about Go is it gives all these powerful concurrency patterns and you can get really creative with it. At this point I don't have to think about it. I just think about the problem I'm trying to solve and there's a lot of really elegant ways to go about it with Go."

Promoted Comments

If you design a language which allows for runtime modification of code it does rather impact on your ability to be fast, except in specific cases. Detecting you're in that case can be harder to do.

If you design a language to provide specific memory guarantees, not guaranteed by hardware, then the pessimism enforced on the compiler to guarantee this can make a huge difference.

If you design a language to allow reflection the runtime must be able to answer, for any legal storage location that contains a reference to an instance of a type, exactly what type it is.

If you design in GC then (for all that it can end up faster in some cases) there are different costs associated with managing this (and some languages actually mandate the semantics more than others)

If your language enforces immutability then you better be able to express whatever problem you have in a way that does not rely on mutation to be efficient (often, even using a persistent data structure, you change the BigO memory characteristics significantly)

Sometimes it doesn't matter, sometimes it renders it impossible, sometimes it just changes how hard it is to hit your performance target.

It's certainly not something to be ignored since, for many (most?) applications, achieving a specific performance target is a requirement[1] to achieve the desired goal. As such there is merit to the discussion, people just often forget that the relative performance rarely matters, only whether it can achieve the desired (and likely future) performance that is required. they also forget it's rarely the only metric, nor even one that matters the most.

167 Reader Comments

Wouldn't it make things easier if "fmt" was implicit so no "import" statement needed?

Only if you think that having unnecessary things tacked on to a language is good design. Go's main goal isn't to make it easy for beginners to write code and for everyone else not having to worry about implicit imports and the interesting edge cases those can bring up (see static imports in java) is nice and any good IDE will give you the option to import things when used anyhow.

IMO, at this point, the only thing Go is really lacking is really good integration with a good full-blown IDE (code highlighting, intelligent auto complete, integrated debugger, documentation viewer, etc).

Wouldn't it make things easier if "fmt" was implicit so no "import" statement needed?

Importing "fmt" is hard? In anything beyond 'hello world' you'll likely be importing a number of packages anyways, so if importing "fmt" is hard for you, you might wanna stick to a scripting language for a while longer until you're more comfortable with programming.

In my honest opinion I think that Go is extremely overhyped. If not for the fact that Google is behind it its user base would most certainly be far smaller. There are plenty of other programming languages which deserve more fame than Go, so why not give them some publicity? Instead of talking about a programming language which has already very nearly reached its peak popularity.

I would suggest writing an article about the Nimrod programming language. It may not have light weight concurrency but then it has no race conditions either. Go's memory model is incredibly prone to races. Can we please stop worshipping "worse is better" now?

Because while a language design is divorced from its run-time platform in theory, that's just not the case in the real world.

If you develop in Java, you are probably going to deploy onto a virtual machine with garbage-collection pauses. If you develop in Python, you'll probably be running through an interpreter. If you're writing C++, you'll probably compile down to the metal.

I don't think anyone quoted in this article is confused about the difference between language and runtime, but developers understand what they mean when they talk about the language's "speed" as a shorthand.

Because you usually can't pick and choose. You have to take one with the other.

Well to some degree. Writing c modules for performance sensitive code in python while still getting the great advantages of python compared to C for 95% of the code is great (and in many more situations you can just use an existing c module, hello numpy)

I would suggest writing an article about the Nimrod programming language. It may not have light weight concurrency but then it has no race conditions either. Go's memory model is incredibly prone to races. Can we please stop worshipping "worse is better" now?

Seems to mainly use async message passing from its own description, haven't really done anything with go either, but how is that much different from channels in go?

Because you usually can't pick and choose. You have to take one with the other.

Technical people should insist on the distinction though. The run-time will improve over time but you'll be stuck with the language for eternity. So if you are gonna rave about the language don't give examples about the run-time. Give examples, like "less typing", "easy to read", etc.

If you design a language which allows for runtime modification of code it does rather impact on your ability to be fast, except in specific cases. Detecting you're in that case can be harder to do.

If you design a language to provide specific memory guarantees, not guaranteed by hardware, then the pessimism enforced on the compiler to guarantee this can make a huge difference.

If you design a language to allow reflection the runtime must be able to answer, for any legal storage location that contains a reference to an instance of a type, exactly what type it is.

If you design in GC then (for all that it can end up faster in some cases) there are different costs associated with managing this (and some languages actually mandate the semantics more than others)

If your language enforces immutability then you better be able to express whatever problem you have in a way that does not rely on mutation to be efficient (often, even using a persistent data structure, you change the BigO memory characteristics significantly)

Sometimes it doesn't matter, sometimes it renders it impossible, sometimes it just changes how hard it is to hit your performance target.

It's certainly not something to be ignored since, for many (most?) applications, achieving a specific performance target is a requirement[1] to achieve the desired goal. As such there is merit to the discussion, people just often forget that the relative performance rarely matters, only whether it can achieve the desired (and likely future) performance that is required. they also forget it's rarely the only metric, nor even one that matters the most.

I would suggest writing an article about the Nimrod programming language. It may not have light weight concurrency but then it has no race conditions either. Go's memory model is incredibly prone to races. Can we please stop worshipping "worse is better" now?

Seems to mainly use async message passing from its own description, haven't really done anything with go either, but how is that much different from channels in go?

Nimrod's channels are non-blocking for a start, so you can't have deadlocks. Also it has thread local GCs which keep realtime guarantees and if you think Go will get that anytime soon, you're misguided as the memory models are quite different.

Apart from concurrency Nimrod also has generics and has managed to implement a syntax without absurd semicolon insertion rules.

I would suggest writing an article about the Nimrod programming language. It may not have light weight concurrency but then it has no race conditions either. Go's memory model is incredibly prone to races. Can we please stop worshipping "worse is better" now?

Seems to mainly use async message passing from its own description, haven't really done anything with go either, but how is that much different from channels in go?

Nimrod's channels are non-blocking for a start, so you can't have deadlocks. Also it has thread local GCs which keep realtime guarantees and if you think Go will get that anytime soon, you're misguided as the memory models are quite different.

Apart from concurrency Nimrod also has generics and has managed to implement a syntax without absurd semicolon insertion rules.

Are there any examples in the wild of Nimrod applications being used in extremely demanding situations? Or even fairly large apps on GitHub to look through?

Personally, I've never seriously considered Nimrod. I've always assumed it was yet-another toy language like BrainFuck, thanks to, well, being named 'nimrod'.

I studied Go a bit. Thought it would be my next language, but I've given up on it for the time being.

My hope was, a cross-platform compiled language without header files, having a simple syntax for working with arrays and hashes. Something like a perl or python, but fast.

A lot of that is true, and so the language has potential.

I can't say the language is elegant though. Because every function can return multiple values, including error objects, sample programs are littered with all kinds of checks to see what they got back. It's not bad, but it isn't concise in the way other languages often are.

There are no objects, there are just functions everywhere. Even functions that make sense only for a given type, are still essentially global functions you have to pull out of the air without context. It feels like working backwards in some way. I'm not sure an IDE could ever really be built to assist.

And in comparison to something like perl or python, Go has very primitive methods for dealing with files. There is no built-in way to read a file line by line, for example. You have to parse the file stream yourself.

And so I realized, Go wasn't a better language than perl or python for the casual hacker; it was actually just a better C. Old-style C. But since better C languages already exist, Go's benefits over them seem very slight to me now.

Go's concurrency model is very similar too and modeled after erlang, It would have been great to get the views of people who have worked on erlang & go. Erlang has all the concurrency benefits of go and has been around longer.

However, since erlang is pure functional it's more difficult for imperative programmers and often gets ignored.

If you design a language which allows for runtime modification of code it does rather impact on your ability to be fast, except in specific cases. Detecting you're in that case can be harder to do.

If you design a language to provide specific memory guarantees, not guaranteed by hardware, then the pessimism enforced on the compiler to guarantee this can make a huge difference.

If you design a language to allow reflection the runtime must be able to answer, for any legal storage location that contains a reference to an instance of a type, exactly what type it is.

If you design in GC then (for all that it can end up faster in some cases) there are different costs associated with managing this (and some languages actually mandate the semantics more than others)

If your language enforces immutability then you better be able to express whatever problem you have in a way that does not rely on mutation to be efficient (often, even using a persistent data structure, you change the BigO memory characteristics significantly)

Sometimes it doesn't matter, sometimes it renders it impossible, sometimes it just changes how hard it is to hit your performance target.

It's certainly not something to be ignored since, for many (most?) applications, achieving a specific performance target is a requirement[1] to achieve the desired goal. As such there is merit to the discussion, people just often forget that the relative performance rarely matters, only whether it can achieve the desired (and likely future) performance that is required. they also forget it's rarely the only metric, nor even one that matters the most.

In practice they are not entirely orthogonal but that just because of implementation choices. Still, though the two, language and run-time, are different enough that the distinction should be made; that talking about one is not the same as talking about the other.

Nimrod's channels are non-blocking for a start, so you can't have deadlocks. Also it has thread local GCs which keep realtime guarantees and if you think Go will get that anytime soon, you're misguided as the memory models are quite different.

Apart from concurrency Nimrod also has generics and has managed to implement a syntax without absurd semicolon insertion rules.

First of all non-blocking channels are only non-blocking until you run out of memory for the channels, so I seriously doubt nimrod makes any hard guarantees on that point (MPI doesn't for good reasons). In any case go also has nonblocking communication, so that goes out of the window.

Only thread-local GCs? Clearly as soon as you allow global or shared variables you will have to do a global GC sometime or you allow memory to grow infinitely large. See modern JVMs for the obvious comparison. Soft realtime guarantees are certainly possible, but for hard realtime *any* GC at all is pretty destructive at least if the timing isn't that easy to guarantee.

I can't say the language is elegant though. Because every function can return multiple values, including error objects, sample programs are littered with all kinds of checks to see what they got back. It's not bad, but it isn't concise in the way other languages often are.

Well sample programs are one thing, but if you look at most production quality code, *quite* a lot of code is generally just there to deal with errors. If go makes sure you can't just ignore all of those, I would actually consider that a plus even if it makes tutorials and sample code longer.. because as we all know most samples and actual production worthy code have generally nothing in common.

I would suggest writing an article about the Nimrod programming language. It may not have light weight concurrency but then it has no race conditions either. Go's memory model is incredibly prone to races. Can we please stop worshipping "worse is better" now?

Seems to mainly use async message passing from its own description, haven't really done anything with go either, but how is that much different from channels in go?

Nimrod's channels are non-blocking for a start, so you can't have deadlocks. Also it has thread local GCs which keep realtime guarantees and if you think Go will get that anytime soon, you're misguided as the memory models are quite different.

Apart from concurrency Nimrod also has generics and has managed to implement a syntax without absurd semicolon insertion rules.

Are there any examples in the wild of Nimrod applications being used in extremely demanding situations? Or even fairly large apps on GitHub to look through?

Personally, I've never seriously considered Nimrod. I've always assumed it was yet-another toy language like BrainFuck, thanks to, well, being named 'nimrod'.

There is the Aporia IDE which is a GTK application and the Nimrod forums. The Nimrod compiler is also of course written in Nimrod.

As for the name, I've never considered it as vulgar as "BrainFuck". To me it's the same as "git", and yet you use git right?

How anyone can prefer statically typed language without generics is beyond me. No really how do you do collections and other general purpose data structures? Casts everywhere? Also their stance on exceptions is not convincing. If I understand correctly they have something like semi-exceptions.

Go's concurrency model is very similar too and modeled after erlang, It would have been great to get the views of people who have worked on erlang & go. Erlang has all the concurrency benefits of go and has been around longer.

However, since erlang is pure functional it's more difficult for imperative programmers and often gets ignored.

Actually it has a very different memory model from erlang; it precisely lacks the feature that makes erlang attractive to me.

Erlang has process-local heaps, which is its single defining feature in my opinion: you can make gc predictable if you can subdivide your problem into erlang processes (roughly similar to threads in other languages). You DO have access to a shared refcounted heap if you need it for binary data.

How anyone can prefer statically typed language without generics is beyond me. No really how do you do collections and other general purpose data structures? Casts everywhere? Also their stance on exceptions is not convincing. If I understand correctly they have something like semi-exceptions.

"head of engineering for Beachfront Media, which recently announced a new platform called Beachfront.iO that serves video ads to mobile devices and tablets"

So this is one of the guys producing crap to eat up all my bandwidth? This is one of the companies making the internet suck more and more for everyone?

If I wanted to watch flashy, overly loud ads about crap I don't care about instead of consuming content I do care about I'd have cable TV or hang out at the mall with the vapid teenagers and their soccer moms.

There is the Aporia IDE which is a GTK application and the Nimrod forums. The Nimrod compiler is also of course written in Nimrod.

I'll have to check them out... although I personally find GTK to be an absolute abomination.

Quote:

As for the name, I've never considered it as vulgar as "BrainFuck". To me it's the same as "git", and yet you use git right?

I'm not saying I object to it because of the name, but that I assumed the author didn't intend for it to be taken seriously (i.e. non-toy) due to the name. Linus Torvalds was able to get away with git, but only because he's Linus Torvalds and he effectively mandated its usage for working with one of the largest open source projects out there. If it had been anyone else in any other situation, git would likely still be virtually unknown today... and at least git sounds like get.

Only thread-local GCs? Clearly as soon as you allow global or shared variables you will have to do a global GC sometime or you allow memory to grow infinitely large. See modern JVMs for the obvious comparison. Soft realtime guarantees are certainly possible,

Yeah, especially if you don't actually bother making them *correct* (in the sense that they don't leak)

The old GC never collected cycles correctly. Fixed but it can cause performance regressions. However you can deactivate the cycle collector with GC_disableMarkAndSweep and run it explicitly at an appropriate time or not at all. There is also a new GC you can activate with --gc:markAndSweep which does not have this problem but is slower in general and has no realtime guarantees.

Also given the breaks/fixes listed there this is not a project you could reasonably recommend to anyone not wanting to take on some serious risk...

Whilst a non programmer POV is possibly an interesting choice, and can lead to a good article quite distinct from the alternates, I have to wonder why you didn't get someone like Peter to do this.

I admit his caustic brand of commentary on programming languages might not have resulted in getting very far, but he has at least attempted to do one of his own for the fun of it, which is kind of a good starting point to being able to do a more in depth interview...

Nimrod's channels are non-blocking for a start, so you can't have deadlocks. Also it has thread local GCs which keep realtime guarantees and if you think Go will get that anytime soon, you're misguided as the memory models are quite different.

Apart from concurrency Nimrod also has generics and has managed to implement a syntax without absurd semicolon insertion rules.

First of all non-blocking channels are only non-blocking until you run out of memory for the channels, so I seriously doubt nimrod makes any hard guarantees on that point (MPI doesn't for good reasons). In any case go also has nonblocking communication, so that goes out of the window.

Only thread-local GCs? Clearly as soon as you allow global or shared variables you will have to do a global GC sometime or you allow memory to grow infinitely large. See modern JVMs for the obvious comparison. Soft realtime guarantees are certainly possible, but for hard realtime *any* GC at all is pretty destructive at least if the timing isn't that easy to guarantee.

Well for now global shared memory is not GC'ed, you have to manually free it so there. But shared memory is highly discouraged anyway. It is well known that proper message passing semantics allow for thread local collection. Go makes sharing very easy though and pretends its idioms make it safe. This is not so.

An example would be to pass a shared closures around. This is forbidden in Nimrod and easy as hell in Go.