top | item 10157244

Best practices for a new Go developer

153 points| torrance | 10 years ago |medium.com | reply

90 comments

order
[+] peteretep|10 years ago|reply

    > You will realize eventually that what you first thought
    > was worth criticizing was actually a deep work of
    > genius.
Funny, I found the more I used it the less I liked it.

    > I’ve seen a lot of criticism of Go’s “shortcomings”
    > from people who are true experts in many languages
    > other than Go.
Cool, normally I just call those people "experts".

    > I can’t recall similar criticism from someone who’s
    > worked with Go at a very deep level for a year or two.
You would do well to understand the meaning of the term "survivorship bias".
[+] SomeCollegeBro|10 years ago|reply
I completely agree - the more I used it the less I liked it. I really enjoyed its implicit simplicity at first. The code I wrote had purpose and was clear, consice, and easy to understand. However, as the codebase grew larger I really ran into the issues more people talk about. Specifically lack of generics. I was using reflection to simulate this, however my performance was terrible on large sets of data. I had to end up relying on a code generator, which seems to be the direction Go is heading.

Edit: Package versioning was another pain point, as someone mentioned below. Their built in package manager is almost too simple. As with anything simple, it's great at first but not so great when more complexities arise.

[+] fauigerzigerk|10 years ago|reply
The thing about Go is that some parts, like channels and goroutines (also some of the library designs and tooling), are so addictive that pretty soon it gets hard to imagine ever having to live without them.

But I don't think everyone using Go really believes or supports this "but in practice it's not a problem and if it's a problem then the problem is YOU!" kind of response to every criticism.

We live in a world of trade-offs. It's disingenuous to act like that wasn't the case.

[+] Kiro|10 years ago|reply
What's with the condescending tone?
[+] oconnor663|10 years ago|reply
> Because Go gives us interfaces and closures we can write much more elegant, generic APIs with a flavor similar to Ruby or Lisp and this is the direction the language naturally wants us to take. Personally I like to use the empty interface for plumbing and only pin things down to specific interfaces or concrete types where I need to for performance or correctness.

That's a lot like using opaque pointers in C. What is it about Go that makes people assume it's shortcomings are beautiful designs?

I learned yesterday that `x == nil` can return false even if x is nil so long as x is an interface type. But it depends on whether x is actually nil or a nil value with a specific type.

(╯°□°)╯︵ ┻━┻

My other pet peeve is that a method with a non-pointer receiver that tries to modify the receiver object will silently drop those modifications on the ground, because the object is copied. Which makes some sense, except that Go likes to convert to pointer receivers automatically, so the caller can't tell that anything is wrong. The only difference is one character in the method definition. Everyone I know hits this bug at some point and loses half an hour before they learn to look for it. You could almost say "all method receivers must be pointers" except that you need to refer to interface types without the pointer.

(╯°□°)╯︵ ┻━┻

[+] eternalban|10 years ago|reply
Go's simplicity is merely syntactical. The semantics can be subtle and not entirely aligned with intuition honed after x years of working with C-like language X.

This is an observation and not a critique.

> I learned yesterday that `x == nil` can return false even if x is nil so long as ...

In other words:

http://play.golang.org/p/YV5ylbQN4j

Thanks for the heads-up! That was news to me.

[+] microtonal|10 years ago|reply
That's called using opaque pointers. It's pretty much what you do in C.

???

The empty interface is effectively dynamic typing, relying on runtime introspection. It does not hide any struct members (unless you make them private), you can simply enumerate them.

Opaque pointers in C provide a way to enforce static typing without exposing struct members and are effectively a method for information hiding.

So, I am wondering why you said that.

My other pet peeve is that a method with a non-pointer receiver that tries to modify the receiver object will silently drop those modifications on the ground, because the object is copied.

I agree. In fact, it is seldom that I actually want the non-pointer variant. If I don't modify the object, just give me a const modifier so that I can indicate that.

(Or reasoned from outside Go: the receiver should IMO always be passed by reference.)

[+] Strom|10 years ago|reply
Everything in Go is passed by value (i.e. copied), even the pointers. The sooner you will understand this, the less problems you will have.
[+] johnnydoebk|10 years ago|reply
I'm not interested in C++, not using it, do not care about it. So, I am neither visiting HN links that are about C++ nor taking part in discussions related to C++.

And I'm really curious what drives people in this thread that are coming to say they do not want to use Go, do not like it, and stuff like that?

[+] lobster_johnson|10 years ago|reply
Go's surge in popularity — and with it, the surge of evangelism — compels people to take a stand, for better or worse. (If Go weren't popular, nobody would bother.)

I suspect it's also partly due to Go's curious mix of maturity and immaturity, in an overall promising package with lots of momentum. Here's a young language that on the one hand is productive to work with and simple to understand, yet so obstinately awkward and inelegant in several other respects, and represented by a team of designers who aren't always saying what many want to hear.

It's clear that Go was, first and foremost, designed for Google and their need for a natively-compiled, concurrent language that steers the user to a very specific path, largely defined by limitations. Its approach prevents language-theoretic cleverness that can complicate a codebase (think Boost or the STL), and it prevents a lot of foot-shooting and optimistic overdesign that newbies are prone to.

But while that's wonderful for Google, that's also a frustrating to developers who don't need this invisible hand hobbling them, and sees Go's potential subverted by the stringent design principles. I admit I'm one of those frustrated by Go's type system, for example, or its heavy-handed approach to error handling.

Part of the frustration is the lack of a clear alternative: Nimrod doesn't have the mindshare, Rust is overly complex and slow to compile, Elixir doesn't have the performance, C++ is, well, C++. Go, for better or worse, fills a kind of sweet spot that, unfortunately, also has a dash of sour. And that kind of frustration easily leads to debate whenever Go comes up.

[+] wyager|10 years ago|reply
>And I'm really curious what drives people in this thread that are coming to say they do not want to use Go, do not like it, and stuff like that?

Last year I wrote this article about why I didn't like Go very much. It was pretty well received here on HN, which is hopefully indicative of it being a good explanation.

http://yager.io/programming/go.html

[+] tunesmith|10 years ago|reply
Probably because of the relative evangelism levels. I don't see many threads on hacker news talking up the virtues of C++.
[+] microtonal|10 years ago|reply
Learn about stack versus heap, and recognize that Go treats the stack differently from other languages.

Note that the Go language spec does not even contain the terms 'stack' and 'heap. Whether something is stack or heap-allocated isn't always clear. E.g. consider:

    foo := &Foo{} // or: new(Foo)
This could be stack or heap-allocated (in the most popular Go implementation) depending on whether the compiler thinks it escapes or not.
[+] pcwalton|10 years ago|reply
> Learn about stack versus heap, and recognize that Go treats the stack differently from other languages.

Yeah, this comment strikes me as deeply confused. Go does not give you precise control over what goes on the stack versus the heap: it's even in the FAQ.

There's a common misconception that stack vs. heap and value types vs. reference types represent the same distinction; they do not. For example, note that it is possible in unmanaged languages to have dangling references into the stack.

[+] drakenot|10 years ago|reply
I was hoping this article would have more substantive advice for new Go developers. It seemed to mostly have very general advice that applies to most other languages like: "Don't try and write language X in language Y. Keep complexity down by not over using complex language features."

I'm writing my current hobby project, a podcast fetcher, as my first project in Go.

The project has been going generally well but there have been a few annoyances so far:

* Why are you not able to easily version git dependencies? Go's solution to this problem is to tell you to create an entirely new git repository for each major version. Really? If they didn't want to go full blown dependency versioning with something like CocoaPods, they could at least let you specify a git branch or tag.

* The db.Sql abstraction does not support multiple result sets. Therefore database drivers, like the popular mysql driver, don't support multiple result sets. This really limits the kinds of stored procedures you can call.

* The debugger support is bad. I have to fall back to using print statements for most of my debugging.

[+] robryk|10 years ago|reply
> Why are you not able to easily version git dependencies? Go's solution to this problem is to tell you to create an entirely new git repository for each major version. Really? If they didn't want to go full blown dependency versioning with something like CocoaPods, they could at least let you specify a git branch or tag.

Take a look at gopkg.in[1]. This is a service that makes branches in other repositories go-gettable. Eg. go getting gopkg.in/foo/bar/v5 will download branch v5 from github.com/foo/bar. This seems to me to be a way of dealing reasonaly well with the issue -- the go get tool itself doesn't need to understand anything at all here.

[1] http://labix.org/gopkg.in

[+] threeseed|10 years ago|reply
"You can make it work, but there is a reason why the Go team chose not to implement that. And, no, it’s not that they are lazy."

One of the many wonderful comments in there.

Those annoyances you listed aren't Go's fault. It's yours.

[+] stonewhite|10 years ago|reply
I was expecting an empty page with a huge "don't" in it. But instead there was interview excerpts from Go developers and a ambiguous list of "best practices".It would be more meaningful if this was backed with some actual practices with some code examples.

I just don't see what people think others would understand by teleologic statements like: "write Go the way it wants to be written"

[+] signa11|10 years ago|reply
> I just don't see what people think others would understand by teleologic statements like: "write Go the way it wants to be written"

well, this is a very general advice, approx. implying : "don't force your previous language's world view while programming in Go".

f.e. if you are coming to Go from say Java, where everything is a class and where hierarchies abound, when you try to write a 'javaesque' go, you would be fighting against the language.

however, embracing a 'goesque' worldview of composition+interfaces as primary vehicle for program design, would/should make the overall experience quite a pleasant one.

[+] rakoo|10 years ago|reply
> I just don't see what people think others would understand by teleologic statements like: "write Go the way it wants to be written"

To me it means "Read what the creators thought about when creating Go and how they view it, and do the same". But really that applies to any craft that exists: instead of trying to apply and adapt what you already know, clean your mind and start from scratch.

[+] Kiro|10 years ago|reply
> I was expecting an empty page with a huge "don't" in it.

Why?

[+] kasey_junk|10 years ago|reply
> I resisted the recommended workspace configuration, as described in How to Write Go Code. Don’t bother, especially in the beginning

I've had the same dev folder structure across platforms, languages, jobs and decades. I basically had to abandon that structure when starting Go. I fought & fought and at the end of the day it is just easier to use their expected workflow. It was (is?) galling but it was the only way to stop fighting the tools and get work done.

[+] XorNot|10 years ago|reply
The intellij go plugin is your friend. Per project go-paths. It's also a good use of make.
[+] jerrac|10 years ago|reply
So, if Go is as bad as the comments so far make it out to be, what are some alternative languages? Specifically, a compiled language that can be deployed without worrying about dependencies. That's the feature that has had me looking into learning Go. I want to be able to just copy one file to my server and run it, no need to install anything extra on the server to make my program work.

Actually, can Go even do that? I think it can...

[+] jerf|10 years ago|reply
"So, if Go is as bad as the comments so far make it out to be, what are some alternative languages?"

While a good question anyhow, it's not as bad as the comments make it out to be. Anything can be made to look bad by only looking at the negatives, anything can be made to look good by only looking at the positives. And the balance shifts depending on what sort of application you're writing. There's a reason that Go has seen a lot of success writing network servers. There's a reason why Go has no penetration into the scientific computing community, and I tend to warn away anybody even thinking about it.

In particular, a lot of the people screaming about Go do not consider some of the positives. For instance, thanks to the implicit satisfaction of interfaces, I find it one of the easiest mainstream imperative languages to still create a separation between IO and pure code, by wrapping all IO behind an interfaced object, one that I may not even have to create (i.e., Files already implement io.Reader and io.Writer, io.Reader & io.Writer also already have several test implementations available in the stdlib and it's easy to adapt them to a few more), which then allows me to write some really good testing code, almost as good as Haskell. (In fact, swapping in alternate implementations is probably easier than in Haskell.) This can be done in other languages, but it often involves jumping through hoops to create your own objects that mirror other object's methods so they can implement an interface or something; in Go it's trivially easy. Between that and the way errors are handled, I find it relatively easy to write very bullet-proof code.

And while I also consider it annoying that Go lacks half of generics (interfaces are actually half of generics, but it's missing generic types), there are also often ways of spelling your APIs so it matters less. My code actually doesn't end up with very many interface{}s in it, and many of the ones that do are "real", in that the code in question really doesn't care what's there.

If you don't put those positives onto the balance, you don't get a proper view.

It doesn't help that, frankly, this was not a very good article and I strongly agree it has tone issues. This brought out a lot of people who might otherwise have just clicked over without commenting.

[+] chadaustin|10 years ago|reply
Haskell is my preferred statically-compiled-but-with-coroutines-and-channels language. It's harder to learn and the tooling is not as good as Go's, but its type system is far more powerful and useful. You can build useful generic abstractions over all types of channels and use them in every context. In Go you either have to use interface{} and pay the perf hit or write a code generator.
[+] pjmlp|10 years ago|reply
> Specifically, a compiled language that can be deployed without worrying about dependencies. That's the feature that has had me looking into learning Go.

Any language with a compiler for native code. All of them support static linking, that is nothing special in the Go toolchain.

[+] kasey_junk|10 years ago|reply
You mean any compiled language with static linking? Don't they all support that?

I mean you can even get the jvm/java apps bundled as a single binary if you really want to.

[+] stonewhite|10 years ago|reply
Statically linked binaries is not a new thing. You can do that on many languages.
[+] jheriko|10 years ago|reply
its not all go specific, despite the comment about 'C style' heap allocation and pointer usage, you will find your C code will get better if you do not do this as well. the heap is a last resort, not the first.
[+] scott_s|10 years ago|reply
I found that comment strange as well - I consider "put everything on the heap" to be more of the default attitude in dynamic languages (Python, Ruby) and in the statically typed managed languages (Java, C#). In C and C++, I default to the stack, and I don't think I'm alone.
[+] adrusi|10 years ago|reply
Yeah, that was odd, I wonder if it was some kind of elaborate typo. Heap allocation is much worse in C than in Go because you're responsible for freeing everything you heap allocate.

There are only a few good reasons to heap allocate in C:

You're writing a library and want to be able to add fields to your data structures without breaking ABI compatibility.

You need to return dynamically sized chunk of data, whose length can't be known to the caller ahead of time, like a string. Still consider returning it by writing to a statically sized buffer passed as an argument, piecewise, as in read().

You need a growable buffer. Still consider allocating a reasonable maximum sized buffer and BOUNDS CHECKING.

Some library requires you to dynamically allocate to use its API. Oh well.

You want to maintain API consistency and that requires dynamically allocating something that doesn't strictly need to be.

Don't be dogmatic, but stack allocation, and occaisionally static allocation are much more maintainable than dynamic allocation.

[+] forgotAgain|10 years ago|reply
I take it these are the idiomatic best practices.