diff --git a/blog/we-have-go-2.markdown b/blog/we-have-go-2.markdown index ed44673..fef399d 100644 --- a/blog/we-have-go-2.markdown +++ b/blog/we-have-go-2.markdown @@ -8,12 +8,11 @@ tags: - modules --- -I've been using Go since Go 1.4. Since I started using Go so long ago, I’ve -seen the language evolve significantly. The Go I write today is roughly the same -Go as the Go I wrote back when I was still learning the language, but overall -it’s evolved and changed into something similar yet different feeling in -practice. Thinking back over the years, here are some of the biggest ticket -items that really changed how I use Go on a daily basis: +I've been using Go since Go 1.4. Since I started using Go then (2014-2015 ish), +I’ve seen the language evolve significantly. The Go I write today is roughly the +same Go as the Go I wrote back when I was still learning the language, but the +toolchain has changed in ways that make it so much nicer in practice. Here are +the biggest things that changed how I use Go on a regular basis: * The compiler rewrite in Go * Go modules @@ -27,11 +26,11 @@ today. Without having met the people I did in the Go slack, I would probably not have gotten as lucky as I have as consistently as I have. Releasing a "Go 2" has become a philosophical and political challenge due to the -forces that be. "Go 2" has kind of gotten the feeling of “this is never going to -happen, is it?” with how the political forces within and without the Go team are +forces that be. "Go 2" has kind of gotten the feeling of "this is never going to +happen, is it?" with how the political forces within and without the Go team are functioning. They seem to have been incrementally releasing new features and using version gating in `go.mod` to make it easier on people instead of a big -semver-breaking release. +release with breaking changes all over the standard library. This is pretty great and I am well in favour of this approach, but with all of the changes that have built up there really should be a Go 2 by this point. If @@ -41,15 +40,16 @@ only to make no significant changes and tag what we have today as Go 2. of salt the size of east Texas. I am not an expert in programming language design and I do not pretend to be one on TV. I am also not a member of the Go team nor do I pretend to be one or see myself becoming one in the -future.

If you are on the Go team and think that something I said -here was observably wrong, please [contact me](/contact) so I can correct it. I +future.

If you are on the Go team and think that something I said +here is demonstrably wrong, please [contact me](/contact) so I can correct it. I have tried to contain my personal feelings or observations about things to these conversation snippets. This is a look back at the huge progress that has been made since Go 1 released -and what I'd consider to be the headline features of Go 2. Most of this is a -whirlwind tour of over a half-decade of improvments to the Go compiler, toolchain -and standard library. I highly encourage you read this fairly large post in chunks +and what I'd consider to be the headline features of Go 2. +This is a whirlwind tour of the huge progress in improvement to the Go compiler, +toolchain, and standard library, including what I'd consider to be the headline +features of Go 2. I highly encourage you read this fairly large post in chunks because it will feel like _a lot_ if you read it all at once. ## The Compiler Rewrite in Go @@ -59,9 +59,9 @@ team has a background in Plan 9 and C was its lingua franca. However as a result of either it being written in C or the design around all the tools it was shelling out to, it wasn’t easy to cross compile Go programs. If you were building windows programs on a Mac you needed to do a separate install of Go -from source with other targets enabled. This worked, it wasn’t the default -though and eventually the Go compiler rewrite in Go changed this so that Go -could cross compile natively with no effort required. +from source with other targets enabled. This worked, but it wasn’t the default +and eventually the Go compiler rewrite in Go changed this so that Go could cross +compile natively with no extra effort required. This has been such an amazingly productive part of the Go toolchain that I was shocked that Go didn’t have this out of the @@ -70,6 +70,13 @@ point where Go didn’t have the easy to use cross-compiling superpower it currently has, and I think that is a more sure marker of success than anything else. +The cross compliation powers are why +Tailscale uses Go so extensively throughout its core product. Every Tailscale +client is built on the same Go source tree and everything is in lockstep with +eachother, provided people actually update their apps. This kind of thing would +be at the least impossible or at the most very difficult in other languages like +Rust or C++. + This one feature is probably at the heart of more CI flows, debian package releases and other workflows than we can know. It's really hard to understate how simple this kind of thing makes distributing software for other @@ -105,19 +112,19 @@ time. ## Go Modules In Go's dependency model, you have a folder that contains all your Go code -called the GOPATH. The GOPATH has a few top level folders that have a well-known -meaning in the Go ecosystem: +called the `GOPATH`. The `GOPATH` has a few top level folders that have a +well-known meaning in the Go ecosystem: * bin: binary files made by `go install` or `go get` go here * pkg: intermediate compiler state goes here * src: Go packages go here -GOPATH has one major advantage: it is ruthlessly easy to understand the +`GOPATH` has one major advantage: it is ruthlessly easy to understand the correlation between the packages you import in your code to their locations on disk. If you need to see what `within.website/ln` is doing, you go to -GOPATH/src/within.website/ln. The files you are looking for are somewhere in +`GOPATH/src/within.website/ln`. The files you are looking for are somewhere in there. You don’t have to really understand how the package manager works (mostly because there isn’t one). If you want to hack something up you just go to the folder and add the changes you want to see. @@ -169,7 +176,7 @@ choice that people really wanted solid guidance and defaults on. After a while they changed this to default to `~/go` (with an easy to use command to influence the defaults without having to set an environment variable). I don't personally understand the arguments people have for wanting to keep their home directory -"clean", but the arguments are valid regardless. +"clean", but their preferences are valid regardless. Overall I think GOPATH was a net good thing for Go. It had its downsides, but as far as these things go it was a very opinionated place to start from. This is @@ -197,11 +204,11 @@ such repository hosting sites. The main disconnect between importing from a GOPATH monorepo and a Go library off of GitHub is that when you import from a monorepo with a GOPATH in it, you need to be sure to import the repository path and not the path used inside the -repository. This sounds weird but this is the difference between importing -`github.com/Xe/x/src/github.com/Xe/x/markov` and `github.com/Xe/x/markov`. This -means that things need to be extracted _out of_ monorepos and reformatted into -“flat” repos so that you can only grab the one package you need. This became -tedious in practice. +repository. This sounds weird but this means you'd import +`github.com/Xe/x/src/github.com/Xe/x/markov` instead of +`github.com/Xe/x/markov`. This means that things need to be extracted _out of_ +monorepos and reformatted into "flat" repos so that you can only grab the one +package you need. This became tedious in practice. In Go 1.5 (the one where they rewrote the compiler in Go) they added support for [vendoring code into your @@ -317,10 +324,14 @@ dependencies in it. It allows you to have `within.website/ln@v0.7` and `within.website/ln@0.9` as dependencies for _two different projects_ without having to vendor source code or do advanced GOPATH manipulation between projects. It also adds cryptographic checksumming for each Go module that you -download from the internet. This allows you to avoid having to shell out to -`git` every time you fetch a module that someone else has fetched before. -Companies could run their own Go module proxy and then use that to provide -offline access to Go code fetched from the internet. +download from the internet, so that you can be sure the code wasn't tampered +with in-flight. They also created a cryptographic checksum comparison server so +that you could ask a third party to validate what it thinks the checksum is so +you can be sure that the code isn't tampered with on the maintainer's side. This +also allows you to avoid having to shell out to `git` every time you fetch a +module that someone else has fetched before. Companies could run their own Go +module proxy and then use that to provide offline access to Go code fetched from +the internet. Wait, couldn't this allow Google to see the source code of all of your Go dependencies? How would this intersect with @@ -332,10 +343,9 @@ disadvantages out of the gate with Go modules. I think that in practice the disadvantages are limited, but still the fact that it defaults to phoning home to Google every time you run a Go build without all the dependencies present locally is kind of questionable. They did make up for this with the checksum -verification database a little, but it's still kinda sus. - -I'm not aware of any companies I've worked at running their own internal Go -module caching servers, but I ran my own for a very long time. +verification database a little, but it's still kinda sus.

I'm not +aware of any companies I've worked at running their own internal Go module +caching servers, but I ran my own for a very long time. The earliest version of Go modules basically was a glorified `vendor` folder manager named `vgo`. This worked out amazingly well and probably made @@ -401,10 +411,10 @@ Semantic Import Versioning has always been a part of Go modules and the Go team is now refusing to budge on this. My suggestion to people is to never -release a version `1.x.x` of a Go project to avoid the “v2 landmine”. The Go +release a version `1.x.x` of a Go project to avoid the "v2 landmine". The Go team claims that the right bit of tooling can help ease the pain, but this tooling never really made it out into the public. I bet it works great inside -google3 though! +Google's internal monorepo though! When you were upgrading a Go project that already hit major version 2 or higher to Go modules, adopting Go modules forced maintainers to make another @@ -436,7 +446,7 @@ could have two versions of the same C functions try to be linked in and this really just does not work. Overall though, Go modules has been a net positive for the community and for -people wanting to create reliable software in Go. It’s just such a big semantic +people wanting to create reliable software in Go. It’s just such a big semantics break in how the toolchain works that I almost think it would have been easier for the to accept if _that_ was Go 2. Especially since the semantic of how the toolchain worked changed so much. @@ -614,8 +624,7 @@ that would normally be compile time errors into runtime errors. I say this as someone who maintains a library that uses contexts to store [contextually relevant log fields](https://pkg.go.dev/within.website/ln) as a way to make logs easier to -correlate between. -
Arguably you could make the case that people are misusing the +correlate between.

Arguably you could make the case that people are misusing the tool and of course this is what will happen when you do that but I don't know if this is really the right thing to tell people.
@@ -629,13 +638,11 @@ really neat if contexts could be goroutine-level globals so you didn’t have to one of the major arguments I remember hearing against them was that contexts "polluted" their function definitions and callsites. I can't disagree with this sentiment, at some level it really does look like contexts propagate "virally" -throughout a codebase. - -I think that the net improvements to reliability and understandability of how -things get stopped do make up for this though. Instead of a bunch of separate -ways to cancel work in each individual library you have the best practice in -the standard library. Having contexts around makes it a lot harder to "leak" -goroutines on accident. +throughout a codebase.

I think that the net improvements to +reliability and understandability of how things get stopped do make up for this +though. Instead of a bunch of separate ways to cancel work in each individual +library you have the best practice in the standard library. Having contexts +around makes it a lot harder to "leak" goroutines on accident. ## Generics