Hacker News new | comments | ask | show | jobs | submit login
Just tell me how to use Go Modules (kablamo.com.au)
331 points by boyter 44 days ago | hide | past | web | favorite | 127 comments



Just going to share a sentiment I've felt while diving into Golang over several months. The canonical way to do things feels like it's all over the place in several areas of Golang. Not in the fundamental "this is how you should indent" bit but in the "this is how you should unpack dynamic json content" sense.

I really wish golang had more canonical content. But at this point, I feel like I'm working against the grain. Golang doesn't feel like a language that wants to prescribe a lot more than fundamentals. How you structure your code beyond the src/git/path/your-code/ is completely up to you and every repo you visit could be different. It's not my thing personally, and I'm leaving this comment just in case any "want to try golang" folk stop by. This is something you should be aware of going in. Don't bang your head when you can't find a clear way of doing something that probably seemed simple in other languages. Embrace the new concept and see if you can get used to it.

That said, if anyone ever breaks into the eco system with an es lint style tool that allows experts to share their best practices via plugins, that would be awesome. If that exists already, please do let me know. I'd love to give it a go.


> How you structure your code beyond the src/git/path/your-code/ is completely up to you and every repo you visit could be different.

This is an unusual criticism. Typically Go is faulted for being too opinionated, and it’s projects too predictable. In fact, this is why I took to the language—no need to see past every maintainer’s personal flourishes just to build the code or run the tests. No ceremoniously nested directory structure. No figuring out how to point the build tool to the source files or how to convince the build tool to output or run a test target. Everything is right there, rather plainly and consistently across all projects. This does mean I have less room for creativity which is an okay tradeoff for me because I have just enough creativity to spend on the application and its architecture and the rest should just work, but I recognize that some may disagree and that’s okay too.


This sounds like tooling and formatting consistency versus abstraction consistency. Go isn't great for building powerful abstractions, so at a higher level implementations will trend towards inconsistency, however consistent the low level syntax.


A few thoughts:

1. I don't think powerful abstractions are often that useful. I use Python in my day job, and we mostly don't use many high level abstractions. We never use monads, for example, and while we do use async futures, these are strictly worse than Go's concurrency features.

2. Pretty sure abstraction power drives _less_ consistency, not more. Effectually, it means there are more ways to do the same thing (and more ways to screw it up and make a mess), and consistency depends on organization discipline and less on the rails of the language. I don't mean to say abstraction is bad--only that consistency is one of the costs, not an advantage.

I'm interested to hear your thoughts.


> Pretty sure abstraction power drives _less_ consistency, not more.

It depends, I think. Abstractions allow users to specify part of a program's behavior independently from others. This is not intended to start a flamewar or pick on Go, but for better or worse Go does not support user defined generic functions. One consequence is that certain kinds of methods are difficult to write in a generic, performant, and type-safe way. If users want performance and type safety, they need multiple implementations of those methods for each desired type. This leads to multiple copies of very similar code (which can evolve independently, lowering consistency) or the use of on of the community-driven code generators (which all have their own differences, again lowering consistency).

Adding generics could largely make all of this inconsistency go away, but as you point out would enable a whole slew of other programming patterns more ways to make a mess.

I prefer to think of abstraction techniques as shifting consistency. Ideally, they provide an obvious way to factor out one piece of a program's behavior, allowing developers to focus on the rest of the behavior independently.


I largely agree, but I think people generally underestimate the abstractive power of (typesafe) Go and overestimate the cost of its abstractions.

Generally Go's abstractions are based on dynamic dispatch (virtual function calls) which are quite cheap and are the basis for most other languages' abstractions. There are languages like Rust and C++ which offer zero-cost abstractions and generally beat Go in performance and abstraction power, but lose in other ways. If you're comparing Go to Java, C#, Haskell, OCaml, Python, JS, and Ruby, Go performs on par or better.

As far as abstractive power, Go does quite well, although there is still some _boilerplate_ (for loops and closures); however, this is generally not where your bugs come from (certainly not your troublesome bugs) and boilerplate is consistent by definition. I think the biggest reason Go's lack of generics irks people is because they want _terse_ code, because terseness is the most obvious (but least useful) kind of DRYness. The next biggest reason is more legitimate--getting a lot of typesafe abstractive power out of Go requires a different bag of tricks (and a different way of thinking about abstraction) than languages with generics.


> never use monads

I think "powerful abstractions" fall into other categories, e.g. ORMs. While gORM kinda-sorta works in the context of golang limitations, it's missing a lot of stuff that doesn't currently fit into the go model.


Can you elaborate? I can't think of anything in Go that would preclude ORMs. I'm not familiar with gORM, however.


in other words, it's consistent if you only look at it for a few minutes. use it for anything and it become an inconsistent mess.


Been trying it out and agree. I like it a lot for simple, stand alone tools. For more complex ones it appears strangely ill-suited and "messy".


As if powerful abstractions lead to more consistency...


You mean 50 layers of abstraction?


>> Not in the fundamental "this is how you should indent" bit but in the "this is how you should unpack dynamic json content" sense

> Typically Go is faulted for being too opinionated, and it’s projects too predictable

The parent addressed this. You are confusing inflexibility of language with inconsistency of ecosystem. Devs can want both more flexibility in the language, syntax, and type system and more commonly accepted project-level structures/approaches at the same time. I definitely don't see projects being too predictable at a general level as each lib has its own way of doing many things.


I agree, I love it even more for making small tools where i can just skip the whole folder structure and just have a .go file in a directory and compile it to a bin without all the fuzz of build tools, special folder structures etc. Of course for bigger, shared projects i know these tools help, but sometimes when you just want to make something small, or test something, i find these tools just get in my way.


go build -o whatever.exe whatever.go works just fine.


When I hear "opinionated" I think of Ruby on Rails. Would be nice if Go had more opinions on structure and conventions perhaps.


Frameworks provide structure; Ruby on Rails is a framework, Go is not.


Go is already halfway there ;)


Even Rails only manages to be opinionated for a small set of aspects about building CRUD web applications. If you can stay within those boundaries, it is useful. But as soon as your application needs to deviate from that, it seems that every project goes off and does its own thing again.


If you frequent reddit/r/golang you'll see this question pops up quite frequently and the comments have many differing suggestions.


Great! What are they?


Comments are snippets of text, typically short, written by consumers of content in order to share their opinion of or reaction to that content with other consumers.


To agree with you with a slight rephrase: what makes idiomatic go code is very murky lately. With new changes in the horizon (Go 2 proposals around error checking and generics) the current solutions just dont feel idiomatic. Before these proposals started coming in, people staunchly (and many still do) defended error checking and lack of generics. Now, it feels a bit weak.

Why am I writing all this ceremony (comparatively little compared to other languages, but ceremony nonetheless) when the new, better way is coming?

Additionally, I agree that the idiomatic design patterns of go aren't well defined beyond "use channels, share memory by communicating, handle errors, and dont ever use functional precepts". A lot of go feels really good which makes the not as good parts really stand out.

Personally, I'd like to see small QoL improvements (beyond generics and error handling) like named arguments, native functional operators like map, reduce, filter that take advantage of go's great runtime, and something cool like kotlin's by keyword for delegating ( https://kotlinlang.org/docs/reference/delegation.html)


> Why am I writing all this ceremony (comparatively little compared to other languages, but ceremony nonetheless) when the new, better way is coming?

Whether or not the new way will be better is far from assured.

I'm outing myself as a bit of a "get off my lawn" greybeard here, but as long as such a large swathe of the industry remains trapped in a permanent state of convulsion where everyone competes to build the next monument to their own cleverness, the unprecedented consistency, clarity and productivity that vanilla Go 1 provides will remain under-appreciated.

There's an awful lot of sound and fury over generics and error handling. I sincerely hope that if and when something is done about it, it isn't done in a way that gives anyone a license to be clever.

> "use channels, share memory by communicating"

They walked back a bit from this, thankfully, and I'm seeing less and less nasty "channels for the sake of it" code these days as a result. The party line seems to be closer to "channels are good and bad, mutexes are good and bad, favour whichever is goodest" nowadays, and the larger projects I've worked on have typically been about 50/50 [1]

  [1]: https://github.com/golang/go/wiki/MutexOrChannel
Whenever I think something is missing, I find it helpful to remind myself that Go is kind of the ultimate "80%" experience. Absolutely everything is 80% complete, from the language, to the tools, to the stdlib, it's performance relative to vanilla C... everywhere. 80%. I also think this is the secret of its success.


Interesting about channels. I had been wondering why, despite always being touted as a core feature and reason for Go being awesome, I find them quite rare when looking at other people's code. I've only found them particularly useful for communication between goroutines on one project (filtering events, sending them to the correct workers), and the rest of the time mostly used to implement timeouts (and wishing for some syntactic sugar there, since it can be non-obvious how to to avoid leaking the timers).


That's a really good point actually, I hadn't thought directly about this before. I've just evolved a position on it without articulating it directly: I almost never include them in my public APIs, almost never see them in the better APIs, and tend to count it as a strike against a package if I see that they're used without strong justification.

I find there's a lot you have to do manually to handle them correctly and it's easy to miss details. I think they're best cordoned off behind isolated, encapsulated parts of a channel-free public interface.

To walk back a tiny bit from my earlier "80%" claim, I reckon channels are probably more like 50% complete. The existence of "context.Context", itself very much half-baked, is a symptom of that.

You have to think long and hard about cancellation and blocking every time you interact with them (even if you decide you don't ultimately need to), and the results, once you've (hopefully) handled all the corner cases can degenerate into a tangled mess that's hard to reason about, harder to test, and impossible to change. I like to call this kind of code "channel soup".

I'd be interested to see proposals for what the 80% version of channels would look like. What we have now is a great start, but it's not great yet.


Channels in Go do not allow to prioritize messages and proper support for cancelation is highly non-trivial. Both are necessary in complex software. With mutexes one can build all these things, but lack of language support for mutexes sucks.


Why named arguments? The parameters should show up in any decent editor showing their type and the name chosen by the implementor. The only reason I can think of for named arguments is to allow for default values to be left out of function calls, which just wouldn't fit in with Go.


The point of named arguments is to be more explicit at the call sites. It's also a way to be more robust against API changes -- you can add new optional named arguments or change their order, without breaking existing invocations. You can use named arguments to make a function interface with many optional arguments -- the kind of thing you would otherwise make a builder style interface for. But that's only the idea; Not saying it is necessarily a good idea to include named arguments.

Many people aren't using "decent text editors". vim isn't one. Webpages and prints of code chunks aren't. Information that you must inquire interactively is simply not the same as static information expressed inline in the code. Such is always a little more typing work but often it's worth it.


I would argue `by` and `data` are the two most powerful keywords from any of the modern languages (~last 10 years) maybe next to `go`.


You should check out the "with" keyword from elixir, (but the #1 all time best elixir feature is the |> (pipe) operator combined with IO.inspect)...


IMO `async/await` (C# / JavaScript), `defer` (Go), and `guard` (Swift) have changed the structure and flow of programs and are commonly used in their respective languages. Why do you believe that `by` and curiously `data` are so powerful?


What does `data` do, and in which language?


I think he means `data` class in kotlin.

https://kotlinlang.org/docs/reference/data-classes.html


Yes, it looks like this. Thanks!


> `data` > modern

Everything old is new again.


I was considering `data` specifically in the context of the JVM. I understand we’ve had structs for ages but the concept of anything other than a reference is so terrribly foreign to the JVM it has made so many really shitty programs noticeably less shitty.


There are a couple implementations of this (e.g. staticcheck[1] is probably the biggest and most widely used), but none are really pluggable or easy to interact with. And nearly anything even slightly sophisticated needs to interact with the type checker, which has like one example repo[2] and no other documentation anywhere.

There's a semi-recent experiment to try to make a pluggable lint system[3], but I'm not sure what the current status is.

For my workplace, tbh I plan on making something semi-hacky just to get us off the ground + use real go plugins so we actually have some minor extensibility without reams of boilerplate. I've built several tiny things that just use the AST (which is not bad at all, and quite fast - several thousand source files take about 250ms), and they've been worth the effort like 100x over, but it's hard / impossible to do anything better without the type checker + significant amounts of work.

[1]: very highly recommended, it has far more / better checks than others I've seen, but is also quite slow and hard to extend: https://staticcheck.io/

[2]: it's honestly quite a good repo, but there needs to be more. it's a fairly unfriendly API and there are few helper libs as far as I've seen: https://golang.org/s/types-tutorial

[3]: https://docs.google.com/document/d/1-azPLXaLgTCKeKDNg0HVMq2o...


I second the comments about the various AST and type packages. They are just absurdly unfriendly.

I've written a linter / code formatter / auto bug repair tool internally for my current company and it sucked. Still sucks. Comments doubly so.

The examples are few, undocumented, and don't always work with the vendor directory concept.

I had to write my own custom importer to get vendor support to be more reliable.

I like the concept of shipping with an AST package; it's very forward looking and useful.

I too hope the go/analysis package could be the answer. I believe it has not merged but has a prototype version that conflicts with master. Maybe they'll patch it up and get it landed soon. I hope, anyways


AST for read access I find relatively decent. Straightforward and not complicated.

For manipulating code tho (e.g. for code gen or rewriting), yeah - absurdly unfriendly fits pretty well, and comments only kinda sorta mostly work until you actually try to produce anything polished. It kinda feels like they prioritized forward-compatibility at the cost of usability (which could be a decent decision / a fair tradeoff, but it sucks as a user trying to do stuff now).


> How you structure your code beyond the src/git/path/your-code/ is completely up to you and every repo you visit could be different.

Erm...I'm confused. Why on earth every repo should have the same structure? I mean, I understand when you use a framework that prescribes you a layout, but even so, good frameworks try not to get into developer's way. Why the source code of a webapp should have the same structure as a, say, database? There were quite a few comments about standard project layout - I'm sure there is something similar for every language, but these things are just recommendations. And thank god they are not rules. Am I missing something here?


Yes. Having a common overall structure is good.

When I open a Maven project, the Java sources are always (99,99% of the time) in src/main/java, the resources are in /src/main/resources, the Java test sources are in src/test/java, etc.

Every project has to have source code, every project could have resources. Every project could have test sources, etc.


I try to adhere to https://github.com/golang-standards/project-layout, but I find that most repositories just have their own way of doing things. I really wish Go provided a standard structure for organizing projects, as I find it increasingly difficult to understand dozens of different structures.


When i first started Golang professionally, over 3 years ago, my first gripe was the GOPATH.

WHY SHOULD ALL MY CODE SIT IN ONE FOLDER? I say gripe, it was a huge annoyance. Why on earth, also, should i need to use the repo path in my structure and imports? It's obscene.

My next annoyance was the utter inability to easily parse dynamic JSON. But you know what the JSON looks like? Great, you'll only have to wrap it in a few structs and check your capitalisation if you want it to parse at all, no problem you think before half a day has gone just parsing one response.

The vendoring makes my month painful as I'm retooling the build system for the flavour of the month vendoring toy.

Structuring large projects still causes me great pain.

All of that said, Golang is now my favourite language to program in, and is my default for any new project.


I've had a similar reaction when trying Go. While I'm biased, I strongly believe Nim offers a much better alternative which solves all of the problems you mention in a much better way.

In particular, parsing JSON is a breeze, just like in Python. And there is no GOPATH weirdness.

Give Nim a try if you haven't already, it may just become your new favourite language :)


I'll look into it, thanks. I was planning on Rust being my next language but I'll see what Nim offers. I take it your bias is because your the/a creator?


I'm one of the core developers


Ok! What's its standard library like compared to Go? I like that I can write powerful projects with few external dependencies. I don't like that C binding is slow and C++ binding is missing.

How does it compare to Rust, which is currently top of my list to learn next?

Can you suggest a reason, or a few why Nim should knock Rust from that spot?


> Golang doesn't feel like a language that wants to prescribe a lot more than fundamentals. How you structure your code beyond the src/git/path/your-code/ is completely up to you and every repo you visit could be different.

I only follow Go casually ATM, but I think that it might fairer to say that the core team wants to stay focused on delivering the language and implementations, so we get blog posts and conference talks, but not a big trove of guidance on patterns & practices.

The best place to interact with the wider Go community is the Gophers Slack, and if you visit there, you will find that there's a lot of knowledge and thinking, but it's in scattered places. For example, this article will probably be cited in any discussion of application structure: https://medium.com/@benbjohnson/standard-package-layout-7cdb...


> es lint style tool that allows experts to share their best practices via plugins,

Have you seen gometalinter? Its defaults are way too computationally expensive for me, but it brings together reports from a lot of different linters.

https://github.com/alecthomas/gometalinter#supported-linters


> in the "this is how you should unpack dynamic json content" sense.

I would be very wary of a language who has core features around such transient things than JSON unpacking.


Our agency most mostly does SaaS apps.

We use Rails and React.

We tried Go for backend but Go is missing ActiveRecorss, payment gateway libraries, etc...

Where is migration management like sqlachemy or activ records?

For building SaaS product i think you should choose Rails.

It takes only few minutes to creare subscription billling, team accounts, invoice etc.. functionality.

I would like to know what you guys use.


Well, the comparison is not really fair, Rails is a framework for building webapp, whereas Go is a language with a standard library.

To simplify a bit: Go is in the same bag as C++/C#/Java, Rails is in the same bag as PHP & [Symfony/Laravel].

If you're mass building CRUD web applications against customers requirements then, clearly, RoR is the best tool for the job (versus Golang).


> Go is in the same bag as C++/C#/Java, Rails is in the same bag as PHP & [Symfony/Laravel].

Go is in the same bag as C++/C#/Java/Ruby/PHP (languages)

Rails is in the same bag as Symfony/Laravel (frameworks)


I literally said 'Rails is a framework for building webapp, whereas Go is a language with a standard library'. what's your point?


Ruby is a language distinct from the Rails framework.

PHP is a language distinct from the other frameworks listed.

Small nitpick, but I always hate to see conflation of languages and frameworks when making comparisons.


For the record, I was not confusing anything here, it was you thinking that I was first listing languages then frameworks, which was not the case.


They moved PHP from framework to language, it’s a small nit but worth picking I suppose.


Thank God, finally a proper "Getting Started" guide for Go Modules. The official docs and wiki page always felt so obtuse and unhelpful about how to actually use the feature, while dep at least offers the obvious "dep init then dep ensure and you'll be fine" solution.


Something which isn't mentioned here and is difficult to find precise information even in the official docs is how versioning works - specifically, how do you specify what version your module is? Semver is mentioned, but not how to define your version.

The short answer is to use git tags. You need to include the `v`, e.g. `v1.2.3`. If you don't use git, I don't know the answer.


versions? 2018 is the year of the evergreen module dependency injection!


Absolutely stupid... I don't want my poduction pipeline to be injecting evergreenshittery I haven't revised/tested.


Not sure if you were being sarcastic...

But this is exactly what 'go mod' is NOT doing. It's called 'Minimal Version Selection'. Quoted right from the lead core dev's blog: ' For stable development, today's build list must also be tomorrow's build list.' (https://research.swtch.com/vgo-mvs).

From what I gathered, 'go mod' is the first mainstream tool which is going that way (but I would be glad to be proved wrong!). Other package manager are using lock files which have their caveats (see https://research.swtch.com/cargo-newest.html for an in depth examination of the revered cargo approach on low-fidelity builds).


The version resolution algorithm is very similar to NuGet's "lowest applicable version" logic, which has been around since 2010. I've seen surprisingly little discussion of the fact they're so similar.

https://docs.microsoft.com/en-us/nuget/consume-packages/depe...


Today I had to write some deployment scripts and the value of a fixed GOPATH really dawned on me. No matter which machine I use, I always know my code will be at GOPATH/src/...

I didn't have to fiddle with any paths or env vars in my scripts. It made my day easier. This is an example of something that seems like a good practice only after you've gotten some gray in your beard.


These global predefined rules lead to long term (several years later) problems. I prefer it when things are configurable and flexible and a technology doesnt try to be the center of the universe. Think of versioning and running multiple versions on same machine. Its like Microsoft with their dll hell. One has to come up with some (undefined, no guidance or built in solution available) way to do it.


Yup, that’s been my own experience too. I’ve really come to appreciate the virtue of GOPATH, and I honestly don’t understand the complaints about it.

Why wouldn’t one want all of one’s code in a single place? What’s wrong with organisation? If one needs to work with different versions, then that’s what version control is for, isn’t it?

Obviously I’m missing something, because my opinion seems to be in the minority. I wonder if it’s because most people are using GUI file managers, which make using directories of code more difficult, while I use emacs and the shell, and so can comfortably manipulate files & directories at scale.


Because different projects need different versions of their dependencies. Do I need a separate GOPATH for each of these projects? I haven't wrangled Go enough to know the right answer to that yet. In my experience with other languages: I'd really prefer to have the dependencies local to the project and not in some global folder that affects all of my projects. I want to be able to make a random project folder anywhere and have the entire thing be self-contained without worrying about anything outside of that folder.


That's what vendor and modules are for.


Go newb here but I personally found it easy to get started with Go Modules and migrate over a fairly large project using Dep. `go mod init` was very helpful here in picking up the Gopkg.toml and doing the magic to make it all work. No complaints at all.


A more detailed intro to Go Modules: https://roberto.selbach.ca/intro-to-go-modules/


There should be a website like "justtellmehow.com" with useful articles like this.

I found that lib and tool documentations written by their authors tend be to be too long. It's understandable since they are passionate about the subject and want to talk about every details, but it's not great when you just want to quickly add a lib to your project or use some tool.


For using Go specifically, there is https://gobyexample.com/ which I have found to be a huge help. I'm not sure that's exactly what you're talking about, though.


This can of course go beyond programming. But, yes, there is a major lack in CS and CE of relaying on the INTENT.

- I can see the datasheet where this micro has 32 message objects and a FIFO mode - but please tell me how you INTENDED it to be used because after weeks of figuring out the obscure memory and buffer system it turns out you never INTENDED a fifo rollover. Ok, that would have been excellent info on Day1.

- Your framework has a couple of examples and each function is documented. Great, but imagine my surprise when it turns out I can’t access a ‘global’ memory object outside of your function because you never intended that to happen - and never thought to let me know.

- This is the entire concept of a WHY comment vs a WHAT comment. Don’t write //increments X+1 instead use //counter ticked here because new value is used during the loop, must be placed at the top, uint8 / rolls at 256 for an x++ example. WHY comments made a big difference in the readability of my code. The goal is to relay intent.

The thing is, you don’t want to rigidly be stuck to someone else’s idea - but it’s extremely helpful to know how they planned for you use this because maybe the choices they made do make sense. Oh use an array of this memory type instead of casting it all to a class/structure! Yea, that’s fine and will be more efficient to search through etc etc.

I don’t need know why but engineers are really bad at telling you what they intended when making the product.


The title reminded me of the "explain deleuze to me" meme:

https://twitter.com/helm_ll/status/991045671133351936


For projects with all dependencies hosted on github. I still need to work out if I can use the new tooling with our internal repos. I imagine I'll need to be editing that go.mod file.


So not only are modules tightly coupled to a specific version control system (git), it’s also tied to a single centralized online service of this decentralized VCS.

Maybe I’m just old fashioned, but to me this is a code smell. It reeks of needless coupling, and I suspect one day (sooner rather than later) the golang designers will come to regret it.

To top it off, despite being tied to a VCS, it seems to lack a useful means of enforcing versioned dependencies.

This simply does not look like a good design. Is this really the new and improved version? If so, how god awfully bad was the previous iteration?


It won't be tied to a single VCS or centralized service. It will support everything 'go get' does, which is several different VCS systems and I think all popular public hosting providers. The trick is getting code from secured, private repos, which takes a varying amount of effort (eg. set up firewall rules and a web server on the domain name you are using in your import paths to return the magic headers to inform 'go get' that it actually needs to use ssh and where on the remote filesystem to go)


I had no problems using it with self hosted gitlab.


It is not tied to Git nor GitHub.


So when you're building on a host without internet access, the "github.com" namespacing isn't an issue?


Depends. If you use vendoring, which is supported by Go modules, then you should be able to avoid needing any internet access. If you want to grab the dependencies fresh, then obviously if you have a github dependency, it will require git and the ability to access GitHub.com.


Thank you. Go Modules seems to suffer the same disease as emacs where people trying to explain it go on and on without getting to the point.


Two commands:

go mod init mymodulename

go build

This will download all the dependencies needed automatically.

You can also do this (from scratch as well):

go mod init mymodulename

go run main.go

And it will still download the dependencies.

If you use a modulename like github.com/dana321/test you can include a subfolder of your project by importing github.com/dana321/test/mysubfolder for example

If you bring down the wrong version (ie. an older) version of a module, just edit the go.mod file and modify the version (after the space where the url is) and change it to master on the line where the module is. When you next run go build or go run it will re-download and re-populate the line(s) you've changed in go.mod with the latest version.

There really isn't much else you need to know!


There is if you want to vendor your dependencies, which given my experience with golang dependency management over the years, I’ll be doing for the foreseeable future.

This guide outlines the set of incantations required to accomplish that & frankly having fought through figuring it out myself from the doco, this would have saved me a bunch of time.


After half a decade of not touching Go I went back to learning it. This was the one thing tripping me up. Not because I didnt understand it but because I cant even compile or pull in packages without defining some compiler variable flag witchcraft. I miss how simpler Go was. The compiler UX there really needs more TLC. Otherwise I still love what I loved about Go: a really incredible out of the box standard library which I highly attribute to Go's success since it allowed people to hit the ground running with Go much quicker.


Was it ever easy? I remember hours trying to figure out what to set GOPATH and GOHOME to and how to get “go get” to actually work. I can’t help but think go is a wonderful language with a poor package manager unfortunately woven directly into the language. Really makes npm feel wonderful in comparison.


I cannot for the life of me understand why they don't make setting up all those variables part of the installer (in the case of Windows, and possibly Mac?) / first time setup process, or define some sane defaults that don't get in your way. I can't help but feel most people likely have a similar enough setup to how I have my environment variables configured at the minimum, why not automate that repetitiveness at the installer / default configuration level.


There is a default for GOPATH these days, it's ~/go on unix.


wasn't the case on mac when i dled golang 2 months ago


Not sure what your experience was. Release 1.8 started the default GOPATH in Feb 2017, which is a tad over two months ago. Maybe you somehow got an older version.


I found it quite easy to set two environment variables and never had issues with `go get` either.


"go get" is a disaster for any project of non trivial size. It was designed with the idea of a mono repo in mind - not a vast distributed network of independently evolving code repositories.


Never had any issues with that as soon as vendoring was an option. I routinely had/have three or more different non-trivial projects I was/am working on, and I dont recall ever having issues with go get. I started around maybe Go1.2. At first, I set GOPATH per project, but as soon as vendoring was a thing, one GOPATH to rule them all. Now, vendor updates to existing dependencies, there is something that has been a complete and utter disaster that hopefully gets better under the mod system.


Can you elaborate ? Which compiler flag is needed for compilation ?


Sorry it wasn't a compiler flag, it was an environment variable.

For Go modules if you don't have 'GO111MODULE' set to 'auto' or something appropriate, your code will whine about not being a module or something ridiculous. It will also cry about pulling in Go code with 'go get' if you don't have that environment variable properly configured.


The blog says that is only true if you put your code in GOPATH and the whole point of modules is that you don't need to do that.


Right, but anyone who is already using GOPATH for all their stuff now had to deal with this bizzare new flag. It was annoying for the few minutes it took me to realize what was going on and to fix it.


The default value of that environment variable is auto.

If you don't set anything, and you are inside GOPATH, module mode is disabled. If you are outside GOPATH, module mode is enabled.

I think that is the most sane behavior. And this flag is needed because modules are an experimental feature.

How else do you recommend to release experimental features ?


It wasn't auto in my case because I was getting errors till I defined it in Windows 10.


I mean that if it isn't set, the behavior is the same as auto. If you weren't getting "auto" behavior, then it's a bug. Please feel free to file an issue.


One question not answered in this article is whether I will need to use modules in the future or not.

I'm perfectly fine with not using them, but I've heard conflicting information about the future roadmap. If I have to use them later, I'd rather switch now, but if I don't have to switch even in 10 years from now, then I'd prefer not to switch to modules at all.

Does anyone know more about this?


Do add the awesome "go mod tidy" command to your blog :-) This removes unused modules and adds missing ones.


> Unfortunately, a quick guide like this doesn’t seem to exist, so I’m writing one here. You’re welcome.

Well, `go help modules` is a good combination of exhaustiveness and conciseness, IMHO.


This site needs better formatting. It looks like the author has section titles, but they're the same font and style as the body.


Also, the text being set at alpha of 0.5 makes it very low contrast and difficult to read, even as a nearly fully sighted person.


The section titles look different from the body text for me, on macOS 10.13 in both Firefox 63 and Chrome 70. Either the site was fixed or your browser is broken.


Please understand that if you vendor your dependencies inside your go.mod project, you own them and will need to support and audit them in the future.

The mod command does not vet any computed hashes of the dependencies found in your vendor directory.


Running "go mod vendor" just populates the vendor folder which allows local development with all tooling. It does not force you to check in the vendor folder (you can even .gitignore vendor). Doing "go mod vendor" is not the typical "vendor", it does not make you the owner and you do not need to support and audit these packages (unless you actually vendor them in the sense of check them in).


You own your dependencies and will need to support and audit them in the future.

Note the lack of conditions on that statement, up to and including the fact this statement isn't even about Go, nor is it even about open source! It is simply a fact. You own your dependencies and you need to support and audit them. You are free to support and audit them by not supporting them at all, not auditing them at all, and blindly pulling in whatever random mutations they may go through in the future. You own the consequences of such terrible support. You own the consequences of good support.


You are correct and I was unclear. My statement is that you and ONLY you now own them. You've essentially forked the project and lost your community.


Though of course if you don't use "go mod vendor" then "go build" will require an internet connection, which is a problem for most distributions (package builds generally don't have an internet connection).


Why aren't your go build tools pulling from the GOPATH/pkg/mod/cache directory?

This would alleviate the need for your build to have a internet connection no?


Fresh builds (in an "rpmbuild" for instance) don't use a cache, for obvious reasons.


Thanks for writing this! I just picked up go again after several months and I had to read the formal guide a few times to correctly figure out how to use modules.

I do like the new modules system so far though!


Golang's not being an opinionated software is the main reason I was not able to passionately start learning this software. Most of the engineers have a bit of a picture in their mind when it comes to implement a specific function. But when the question comes "what is the best way of implementing it in Golang?" then unfortunately the answer is "Do as you want". This prevents me learning the power of language from the beginning.

Being opinionated is about onboarding people with the best experience from the experts of the language, then let people overwrite or choose their own way of doing things when they know what they are doing.


> Golang's not being an opinionated software [...] "what is the best way of implementing it in Golang?" then unfortunately the answer is "Do as you want".

I'm really not sure. Idiomatic Go is something well covered. Gofmt is ensuring consistent formatting. The official docs have an entry dedicated to this: https://golang.org/doc/effective_go.html.

And what would be an opinionated language if Golang is not?


Rust comes to mind. Almost all projects fetch dependencies and build the same way (cargo), with very similar looking project structures and entry point. The community has rallied around a set of libraries that's used by everyone.

I couldn't care less about formatting differences between repos. Ignoring superficial formatting difference, Ruby's community also has one package manager (bundler) and build tool (rake) with a widely followed naming convention and directory structure and community consensus set of libraries.

Both the above languages I can build a codebase I've never seen before and have it running within minutes even without README.

Go feels like the wild wild west. Everyone has their own directory structure, with different ways of fetching dependencies (modules vs dep), building (seems to have consolidated around go build vs make), with everything hand rolled or whatever NIH selection criteria they used for their libraries.


> ways of fetching dependencies (modules vs dep)

Yes, an 'official' package manager have clearly not been a target for the core team for a (way too) long time, whereas Rust has envisioned that 'community' part from the beginning.

> I couldn't care less about formatting differences between repos.

Whereas, to me, an official, no-question-asked tool like 'gofmt' is godsend. It really depends on what you're building and in which context.

> , building (seems to have consolidated around go build vs make)

Not sure here, 'go build' is clearly the idiomatic building way from almost day one.

> Both the above languages I can build a codebase I've never seen before and have it running within minutes even without README.

That was rather not (enough?) straightforward under %GOPATH% era, indeed. Today, with Go modules, its a one liner 'git pull; go build', granted you have Go 1.11 available in %PATH% and that the codebase already have a go.mod file (if not, a 'go mod init <pkgname>' should fix it).


Thanks for the reply. Maybe it has changed over the time but what i have meant was mostly about the way an application is built with golang.

Let me give you an example, it might be easier for me to explain it this way.

Writing a web application that responds to a post request. I am guessing I can use net/http library to achieve that. But does it implement CSRF prevention automatically, or should I be already informed about including it as a developer?

I would be grateful for letting me understand this part. I am not against the language, but just curious.


> Writing a web application that responds to a post request. I am guessing I can use net/http library to achieve that. But does it implement CSRF prevention automatically, or should I be already informed about including it as a developer?

I think I get where you may come from. Indeed, Golang (the language and its standard lib) is not a framework-language toolkit ala RoR. The standard lib is providing building blocks with really straightforwards and clear documentation (check https://golang.org/pkg/net/http/#pkg-overview).

> or should I be already informed about including it as a developer?

I guess you really should, whatever language/framework you're currently using.

> what i have meant was mostly about the way an application is built with golang.

Yes, clearly the confusion may be here, I took it as "the language is not opinionated", whereas you were in fact saying that the language and its standard library are not a universal web application toolkit/framework.


Having learned to code in a time and age where being opinionated wasn't a thing, opinionated languages feel like golden cages.


I am new to Go and I want to start a new project.

In which cases should I adopt modules versus just sticking with $GOPATH?


Everything is moving to Go modules; unless you need compatibility with legacy tools I'd start updating and go ahead and use modules whenever possible. It will make your life easier in the future when you have to transition fewer things.


Is there a decent convention for naming your specifically-for-deps module?

‘go mod init vendored’?


You should probably still be using a full module init path (eg. myrepos.example.com/mymodule) to make it go get compatible, and regardless of whether it will only be a dep for a single project or not, the name should be descriptive of what's in the module or package. Having a module that's "specifically for deps" sounds wrong to me in general, but without knowing what you're actually doing that is harder to give advise on.


actually

> go get -u ./...

should probably be

> go mod tidy




Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact

Search: