Golang — The annoying remote import path

c9s
4 min readMay 15, 2016

--

The “import” syntax is commonly used in many languages, like Java, Python or ES6 (ECMAScript 2015). What the “import” does is pretty simple — importing something from another package into the current space.

However, the “import” syntax designed in Go works a little bit different from other languages. That is, you can import a package from a remote path like “github.com/c9s/somepkg/subpkg”.

As you can see, the import paths bind to the domain name and the URL path. When you run “go get”, the golang command tool automatically fetches the repository into the current workspace and start compiling your packages.

Sounds pretty nice, right? No. It’s not going to be like that. In the real world, it’s terrible. People start using these remote import paths in a lot of packages, and each package depends on other packages “remotely”.

Chances are, people will move their packages to a new domain, a new site, or a new location.., packages could be broken when paths are renamed or changed. And once your import paths are bound to the URL path, it’s pretty annoying to change them to make the package compilable/usable again.

If you’re working on a golang-based project privately, it’s also hard to publish them to GitHub. Say, you need two replicates, one private repository, which you push commits more frequently, and the other public repository, which you only push commits only when something needs to be released. By the design of “go get”, you can only choose one domain name to use in your import path, you have to rename all your import paths with “github.com/blah/blah” and your package will bind to “github.com” forever.

For people who wants to fork Golang projects on GitHub, it’s also pretty annoying to manage the import paths because once you fork the project, the self-referencing remote paths needs to be changed to make things work.

I am not talking about dependency management, although it’s still an issue even with “gb”. I mean, If you have a large package that contains more than 1 level namespace, you will also have this issue.

For example, If I have c6/ast, c6/parser, c6/runtime packages, I have to declare my import paths like

import “github.com/c9s/c6/ast”import “github.com/c9s/c6/parser”import “github.com/c9s/c6/runtime”

… in all my package files and not just “import c6/ast” or “import “./ast”.

I hate these domain names in my import path!

Even so, for this reason, I created a project named “goenv”, which creates an isolated workspace for each project, to let me remove the URL path dependency. However, I don’t know why people on GitHub keep sending PRs (#77, #69) enthusiastically to rename all my package import paths into “github.com/…” format to make their “go get” works, even I described the reason why I didn’t use the remote import path in the README.

This “remote import feature” has became a nightmare for me, it doesn’t work well with private packages and you can’t upload the packages to more than one site because the import paths are bound to the domain. And it’s hard to replicate the package mirror, to provide a custom authentication for fetching packages or to move/rename the paths.

I’m not saying that Go is bad or Go can’t be used for development, I have to say, all other parts are really good, simple, concise and the build speed is very fast. However the remote import design must be changed, or this problem won’t be solved. Not only me, I’ve heard a lot of people saying that the import path is a bad design on Facebook posts and Tweets.

I believe this design could be improved by the Google team, but there is a long way to go, not just 2–3 years, because there are already a lot of people using “go get” and actually there was a proposal for it and it was rejected, the reason from Rob Pike was:

Not supporting local imports was a very deliberate decision.  We understand the
advantages, but there are also disadvantages. The most important one is that you cannot
tell what a source file imports unless you know where it was found: copying a source
file changes the meaning of its imports. This is not a good property for a language
that is trying to be explicit and clear about dependency management.

So, sorry, but we're not going to allow local imports in go get'able code.

Also note that when you use local imports in other code, it means you are giving up on
incremental compilation: the object files for local imports are never installed, so they
cannot be reused.

In the end, I decided to abandon the Golang project — c6 and have decided to pick up a new language, probably Rust to develop new projects for the need of performance and concurrency. (Rust now runs more than 3–10+ times faster than Go in most cases)

--

--