Don't couple your Go code to GitHub

320 points - yesterday at 4:50 PM

Source

Comments

jerf today at 1:29 PM
This is hiding in the obfuscation of being overly specific. The problem is general. To name a package, one must be in a namespace of some sort. As the universe does not provide any sort of abstract ambient "namespace" we can all appeal to, all namespaces are human constructs. As human constructs, they may fail.

In this terminology, the article basically amounts to saying "This namespace can fail! The solution is, use another!"

But that doesn't get you anywhere, because the new namespace can fail too. In fact it is almost certain that it will fail sooner than "github.com's DNS and hosting" as a namespace.

There is no solution where you tie your software release to a namespace that can't fail because there is no namespace that can't fail. It doesn't matter if your favorite language uses DNS or has a centrally blessed repository or if it distributes a blessed list of package names with the language itself or anything else. The namespace can fail.

Therefore, the only thing you can really do is be resilient against failures, and in a lot of ways, the only practical solution to resilience is just to assume that if, in the future, someone has problems getting a package due to a namespace failure, they will not helplessly disintegrate into a puddle of tears while your code is lost forever, but that the future person will instead solve this perfectly solvable problem.

thih9 yesterday at 7:47 PM
> In my opinion, every commerical software development team using Go should be using custom domains for namespacing their internal libraries and packages.

I’d remove “go” from the above, i.e. I think same applies to other stacks.

Even using GitHub domain links in code comments gets problematic long term. Ie when a migration happens and those links start pointing nowhere.

dewey yesterday at 8:20 PM
> That is, if you move your git hosting to GitLab then you have to change your code!

You can also just use "replace github.com/example/example => gitlab.com/example/example" in your go.mod file and everything will keep working. That seems like a very pre-mature optimization for something that doesn't really matter.

p4bl0 yesterday at 10:20 PM
True, but beware of the domain name you're using. Because VeriSign may unilaterally decide to delete your domain name along with thousands of others [1] and you're back to square one…

[1] https://neil.fraser.name/news/2026/09/03/

st3fan yesterday at 11:37 PM
Great when a company goes out of business and the domains are dangling. Next one to scoop it up takes over source code that others new depend on. We've seen this many times already in other ecosystems. It is a very bad situation.

It is bad advice to move your packages under your own domain. You will never be as good as Microsoft to keep paying for the domain. There are no guarantees in life but I do guarantee you that when you go out of business, that domain is the last thing you will think about.

SenHeng yesterday at 6:06 PM
GitHub is almost forever. Your custom domain disappears when you stop paying the bills, which if you’re an open source developer has a higher likelihood than GitHub disappearing.

One day, we’re all going back to vendoring dependencies.

rot256 today at 1:22 PM
Package management in Go always felt pretty hacky. Among the issues is the confusion between "what something is" and "where something is". At the beginning there wasn't even a way to have different versions of a package, with the justification that you should just fix an interface and keep it stable till the end of time, there was also no way to "lock dependencies" so at build time you got whatever that repo served you. It has gotten better since, but still some jankyness as a result of "growing" a package system.
unscaled yesterday at 10:31 PM
> One of the good features of Go is that you namespace your code with the location to fetch the code.

Then the rest of the article explains why this is NOT a good feature in practice.

I don't think it's unworkable either, but this is one of these little thing that Go decided to do different and convinced its fans that this is a great idea and all the other languages where doing it wrong. After a couple of road bumps appeared, instead of admitting there are some advantages to having official package names, we're now told that everybody should just set up their own custom domain with an nginx server or a Go Vanity URLs forwarder to serve traffic for their GitHub-hosted packages.

gumby yesterday at 7:51 PM
Seems like an error to use a URL. This is perfect for a URN or some other form of URI.

It could be a URN that used the DNS as a back end (though that’s a lot like a URL) so better would be something more abstract with multiple possible resolvers and a signature.

0xCMP yesterday at 9:47 PM
This is great and I think for any company/individual who is going to ensure their domain is registered and maintained this makes a lot of sense. It would be catastrophic, but there is no reason Github couldn't disappear or otherwise change some policies that require moving away from it. The way Go works makes committing these package names tied to Github so much more weighty than simply the place you pull from.

The one thing that I was worried about was returning 301 in the example Nginx config. If you ever wanted to change the url that clients are redirected to then any browsers that visited the old url config would be forced to go to the old config's redirect url. For `go ...` and `curl` it wouldn't matter, but Chrome/Firefox will cache that 301 permanently and break the intended redirect. Not sure if this is really an issue in practice though.

Cthulhu_ today at 1:54 PM
This tries to solve the hypothetical problem of a library designer moving their codebase to another domain and breaking imports. But, this rarely happens in practice, so unless you plan to move hosting in advance, it may not be necessary.

That said, in the normal run of things you can mark your last release on e.g. github as deprecated, leave a note that it's moved to xyz, and wait for the users to migrate themselves. Migrating is fairly trivial in that it's a find / replace.

prasadvara yesterday at 5:00 PM
Isn't this going to introduce the dependency of domain management? I understand the positive side of it, but put some infra level manamgment layer to individual Open Source devs, just a thought. But yeah, positives vs negatives weigh and pick.
MadVikingGod yesterday at 11:50 PM
Here's the primer for those who thing find a replace is the answer. Pretend you have a project with A 1.2.3 depends on B 2.4.6 which depends on C 0.1.1. If you are in github you get module github.com/example/A requires ( github.com/example/B 2.4.6 github.com/example/C 0.1.1 )

You now have many choices on how to proceed, but none of them will include A) being able to build old releases, or B) doing so without making changes to all dependencies.

One choice is go to the leaves, C in our example, and make a release on gitlab. Then go to B, and have B depend on the gitlab C. This is fine for rolling forward, but if you want to roll back you would have to rewrite all of C to use the gitlab url, and find all of the equivalent tags and repush all of them. Also tell your users that any binaries you've released now have new hashes. Then repeat this for every repo. It's not a small task.

blackjpi today at 4:25 AM
I don’t think I would use a 301 permanent redirect for this sort of thing. If you later want to switch from GitHub to some other provider, software that aggressively caches permanent redirects (like browsers) will remember the old redirect to GitHub, especially since you don’t have any cache control headers set. Maybe not as large a concern will CLI tools, but something to be aware of as this has bit me in the past.
deanc today at 11:58 AM
That's all well and good, but what happens if you drop off the face of the earth due to personal issues, health issues or worse and your domain expires?
nirui today at 7:14 AM
A GitHub URL at least is still an credible identifier, your custom domains is not, and likely will never be given how the system is accustomed to. Domain is for branding, not identification.

Unless of course, it's special domains that has identification built in, such as .onion which is generated in such way (cryptographic keys) no other people can easily obtain control even after the domain is no longer maintained.

If you really don't want to use GitHub, an alternative is just use .internal suffix (i.e. yourproject.internal/project) in combination with `replace` directives in go.mod. But that require your user to manually download/install your package and then edit their own go.mod.

pulkitbanta today at 2:43 PM
I always wondered when I moved from NodeJS to Golang on why do I need to import everything from github. It would have been simpler to have a central registery to host your packages and import from there I think with secrets to read the private ones
pidgeon_lover today at 9:52 AM
"Package manager"-like behaviour like this is evil. Importing software/code at runtime from the internet (like Apt, Go, Pacman, Pip, NPM, etc.) is the equivalent of those dreadful online stub installer packages, and it's impressive it's as trusted a mechanism as it is.

Leave the Go code or Linux distro to link-rot for a couple decades and all imports like this will be dead or serving malware, leaving the software unusable.

maxdo today at 3:49 PM
migration concerns is a task of any coding agent for 2hours with testings, a cheap one for such an easy task, grok, gpt 6 luna can easily do it with almost no tokens cost.
motbus3 today at 12:36 PM
Holy cow. When I was learning go I thought I was the only one crazy to think that is not a good default
hk1337 today at 12:24 AM
It's a rather internet focused language. Even if you don't point it to github, some other packages will and you still have to point it to something (i.e. gitlab, codeberg, or your personal git server). Then what happens if those servers are no longer available?
dirkc today at 12:29 PM
I've been saved by the commit hash in the dependency when reviving an old Python project. I could locate a fork of the repo and use the same version to get the code going again. I ended up vendoring the dep.
okanat today at 12:43 AM
Just like [^1] says:

> After spending years doing those FFI dances in both directions, I’ve reached the conclusion that the only good boundary with Go is a network boundary.

It looks like this extends into the package manager too. The only good boundary with Go programs is a socket. The only good boundary with the Go package manager (? if we can say it is one) is a domain and a DNS server (your own or somebody else's in the case of gopkg.in).

[^1]: https://fasterthanli.me/articles/lies-we-tell-ourselves-to-k...

alexfortin today at 11:49 AM
Thanks for the article, it inspired me to come up with my own solution (and blog post):

https://a.l3x.in/blog/vanity-go-import/

farthest today at 1:18 PM
So what happens when the domain provider gets bought and the domain in your OSS project gets auctioned and compromised? This is turtles all the way down it seems..
gchamonlive yesterday at 9:07 PM
Thanks for answering my question! So yes, if I intend to use go and study the packages I'd have to rely on beforehand, if they're all hosted on GitHub should make me steer away from Golang. Or at least plan to cache the packages I need locally so CI always have something to work with when building.

Alternatively, could we ourselves build an automatic mirror so there's a redundant supply provider that doesn't depend on a maintainer's choice of git forge

https://news.ycombinator.com/item?id=49434625

andreashaerter yesterday at 6:14 PM
There are also a few Hugo templates for vanity import paths.

Here's mine: https://github.com/foundata/hugo-theme-govanity (e.g. used at https://golang.foundata.com/ )

And yes I'm aware of the irony of hosting this on GitHub... still figuring out a good workflow for maintaining our OSS on Codeberg and GitHub in parallel fed from the internal forge. The dependency on our own domain is real but we favor it.

shieldagent today at 12:36 PM
We hit this after an org rename. replace covered our modules; transitive imports of the old github.com path still broke builds.
abhirajabhi312 today at 3:11 PM
i would like to talk about the article's main point of not tying things to github only , so it’s basically distribution vs theoretical decoupling. Sure, tying the module path to GitHub creates some lock-in, but GitHub is where the community and visibility already are.

"This namespace can fail! The solution is, use another!" while i agree with this but setting up different namespace just because you want to avoid vendor-lock , we need to think from maintainer's perspective as thi would introduces frictions and so are of the most open-source maintainers willing to take it or not?

imhoguy today at 7:27 AM
I think we need better handle to the original location/author/project than a domain. Any of such "go" proxy is yet another SPOF.

We should lean towards IPNS (Name Seever part of IPFS) or some distribited ledger.

serbuvlad yesterday at 10:58 PM
> That is, if you move your git hosting to GitLab then you have to change your code!

Not to be cynical about this but I fail to see how running sed s///g after a very rare event qualifies as a serious problem.

Now, sure, given that the solution is so simple, it's a nice recommendation, but still...

tugback today at 8:51 AM
Definitely had a private GitHub dependency disappear once. Vendoring saved us a massive headache downstream.
grumpy_coder today at 2:00 AM
How many other people have hit the pain of injecting ssh keys for gitlab into a docker container building your code in CI?

Compiling code shouldn't hit network by default.

mosselman yesterday at 9:04 PM
I have not worked with Go, so I thought I'd ask: why wouldn't simple find and replace be able to fix this? And why wouldn't a coding agent be able to do this for you quickly?
popcornricecake today at 12:12 AM
I fear one of these days someone will copy a legit package and make it onto your search results, and then the Go tools pull in something extra.
nizarmah yesterday at 9:37 PM
That's a beautiful solution. I love it! The alternative work went with was to just build everything ourselves.
lewo today at 7:44 AM
Another way to fix this kind of issues would be to fallback on the Software Heritage which archives the whole GitHub.

This is something Nix and Guix are trying to achieve: by default, they fetch source code from the original endpoint, but if it doesn't respond anymore, they would query the Software Heritage archive [1].

Because go mod doesn't use SHA, it makes things a bit more complicated but Nix/Guix encountered the same issue and there are some mechanisms to workaround this issue.

[1] https://www.softwareheritage.org/2025/05/21/software-heritag...

silverwind today at 8:26 AM
I disagree, prefer to see directly which forge a module is hosted on.
deleted yesterday at 8:29 PM
bionsystem yesterday at 8:11 PM
braid has been a treat for us to manage external dependencies. Much cleaner than submodules.
drunken_thor today at 12:02 AM
Honestly google should have always had a package index even if it points tooling to the repo, but it would maintain the package name with the ability to change where the repo is.
globular-toast today at 6:05 AM
So you need a domain and an nginx server running somewhere? That seems like more of a liability than just controlling a domain. Isn't there a way to configure this this just using DNS and no web server?
verdverm today at 3:11 AM
fun fact, in the process of designing CUE modules, the Go team said they would use a registry if they could do it over, CUE uses OCI registries

MVS + OCI is the gold standard imo

CUE module proposal (implemented a while back) - https://github.com/cue-lang/cue/discussions/2939

MVS, the fast and deterministic dependency resolving algorithm Go uses instead of SAT solvers - https://research.swtch.com/vgo-mvs

cyberax yesterday at 11:16 PM
I'm not sure that custom domains are a good idea. It's way too easy to end up with broken links once vanity domains go away.

However, Golang saves the hashes of all the modules in `go.sum` files, so we just need to add a way to do content-addressable fetches. This solves the issue of reproducibility for old builds. As long as you can find the module in some repository somewhere, you'll be able to build it.

The next step is supporting module _evolution_. We need a way to declare: "From this point onward, `github.com/company/someproject` is now `company.com/someproject`", so that all the references are to these packages are identical. This is possible on a per-package basis with `replace` directives, but this doesn't scale.

And this is not easy to solve in general (especially in the age of supply-chain attacks). If the initial project cooperates (or if Github can be convinced to help), perhaps at least a part of this can be solved by adding special "redirecting module" support to Go.

0xbadcafebee yesterday at 8:13 PM
Even better reason: you can later point this domain at an artifact registry. This not only gives you reliability and flexibility, it also secures your software supply chain. You don't need an SBOM or anything fancy to get started, just pull all your artifacts into a central source and improve it over time. Install an artifact registry anywhere you can run a container, use dumb static shared credentials, and start with "proxy mode". Later on you can pin or restrict versions, verify checksums, implement SSO, etc. This is going to become table stakes in the new security landscape.
skybrian yesterday at 8:09 PM
Not really convinced. I'd rather it stayed on Github so it doesn't disappear. Particular for businesses whose priorities might change.
PunchyHamster today at 10:19 AM
Now you coupled it to 2 services you don't own, congratulations on making it worse.

Bonus points if DNS mafia deems your cool TLD to now cost 10x times more coz it's "premium" and you're stuck paying up or telling everyone to migrate

bigwhite yesterday at 10:36 PM
[flagged]
ambicapter yesterday at 10:34 PM
[dead]
ArgumentMoney81 today at 7:03 AM
[flagged]
GnosiWorks today at 8:32 AM
[dead]
Meneth yesterday at 9:25 PM
You shouldn't be using fully qualified domains at all. Use relative paths instead, like sibling-hosted git submodules.