Go best practices, six years in

  • The stages of go enlightenment:

    1. Holy crap, this is like coding in early Java days, what the heck were the language designers smoking?! They just ignored everything! Where's my testing framework! DI?! Build system, dependency management?! WTF.

    2. Holy crap, this is like coding in the early Java days! This is awesome! I can understand all golang code I read! Everything is so simple and easy. I finally get "less is more", and "worse is better"!

    3. (Many months later) Oh god, I'm getting so sick of writing this test assertion in 4 lines over and over, and writing 100s of lines of mock structs. My tests are 5% substance and 95% setup. Okay let's write a junit/mockito assertion library. Oh crap, I'm having to manually wire everything everywhere and it's such a pain. Let's write a DI library. Ugh my project is getting too big and `go get` just doesn't cut it - time to use a dependency management library. Ugh, makefiles only go so far, time to write a build tool. Ugh code generation and metacoding is nonexistent, let's write our own code generation tool so we don't have to hand build 100s of types. ETC.

    4. (Distant future) As golang's stubborn designers finally accept some flaws in their original thinking, they add features to make the language look a lot like another widely used language at the Googplex. Mission accomplished?

    P.S. This is somewhat tongue in cheek - I do enjoy using golang day to day. It is a refreshing change :).

  • The article sort of glosses over IntelliJ with the golang plugin as an "other" IDE, but it's best in class hands down. At one point in my past I had sworn off of Java-based IDEs, and used Sublime Text for years with primarily Python and Go. Something convinced me to try Go in IntelliJ, and really it's fantastic.

    It also covers "important-to-me" features the author mentions. I haven't tried VSCode yet, but have tried all the others listed. IntelliJ is fantastic for Go.

  • > Only func main has the right to decide the flags that will be available to the user.

    This one applies to every language. I was working with a python package that decided that, since `sys.argv` was available from anywhere, it should parse `sys.argv` to configure its own behavior. For a long time, their suggestion was to first parse `sys.argv` yourself, then to modify it before going into the library.

  • After working with .NET/Java/Node.js/Ruby/Python etc the move to Go involved a larger investment in time. I found this really informative and it's great to have the learnings condensed down.

  • Good article on the whole, but I have a few quibbles:

    > If your repo foo is primarily a binary, put your library code in a lib/ subdir, and call it package foo.

    IMHO, that's just ugly, uglier than foo/foo or foo/foolib or foo/libfoo.

    I also think that anything which has commands other than a single-command project which will always be a single-command project (there are fewer of these than one might think …) should put all commands, even a single one, in cmd/.

    I think that inline config objects should be used with useful zero values to try to emulate Lisp's or Python's keyword arguments: if one always needs to provide a value for each config object member, then just use arguments after all.

    I think a testing library can be a great addition, since it can turn a three-line if check into a one-line assertion.

  • It was a great talk at QCon London earlier this year - the video is due to be published towards the end of next month. I'll try and come back to this thread when it is with the URL.

    (Disclaimer: I was the track host for Perer's talk)

  • "those parameters should be part of type constructors"

    I'm not sure if this is a nit, a misunderstanding on my part, or a difference in terminology, but I think what is meant here is "value constructors" (or more commonly, just "constructors"). As I understand the term, "type constructors" construct types.

  • Libraries using log.Printf are incredibly tough to work with in a production environment if you want anything more interesting than looking at stderr. Libraries that let you provide a logger API at time of construction are better than log.Printf, but they still fail at letting you include contextual information inside method calls.

    We inject a logging interface into all our methods that take a context.Context object [1]. This allows us to push contextual information onto the stack of context objects, and then when we log we can do it at the point of failure and have access to an immense amount of useful information. Check the attached gist to see an example of how this works in practice [2].

    Given that the context library originated out of Google and is now part of the stdlib in 1.7, I would love to see other libraries embrace it instead of relying on much less flexible solutions.

    [1] https://godoc.org/golang.org/x/net/context

    [2] https://gist.github.com/justonia/f81eead323d2b23eca1c485ed8e...

  • > No statements go by where the object is in an intermediate, invalid state.

    This seems kind of misleading. Per my understanding of Go, omitted fields in a struct initialization are defaulted not reported as errors. So you're equally likely to be passing invalid state, in the two situations.

    One pattern I like in Haskell, for this kind of thing, is to define a defaultConfig value that contains typical defaults and which can then be tweaked as desired.

    One big advantage this has is that when some functionality is made newly configurable, you set the default to be the old behavior and existing code continues to work correctly unmodified without any additional effort.

  • > That advice still holds today: vendoring is still the solution to dependency management for binaries.

    This might be a stupid question, but can someone explain to me what this means? Thanks!

  • What you need is... a [goat](https://github.com/mediocregopher/goat)

  • Looks a lot like a language designed by a committee, but I generally like it.

    I don't love it, like I love Python, but it does its job and it is fast.

    What I really like is the defer(), the channels(having the option for asynchronous channels would be awesome), the go routines(async callback hell is getting old).

    What I find completely awkward however is the enforced first letter capitalization(don't tell me how to live my life, go!), the interface{}, and the GC.

  • Slightly tangential, but, could someone share their experience using Go specifically for building websites?

    How does Go (including Go frameworks specifically geared towards web development) compare in terms of performance, ease of development with RoR, Laravel?

    Is building websites using Go a good use case or is Go better suited for building high performance microservices?

  • The site blocks Tor users; ugh.

  • undefined