Some thoughts on Go's unusual approach to identifier visibility
A while back I read Jesse Duffield's Go'ing Insane Part Two: Partial Privacy (eventually via). In this installment, Duffield is frustrated by how Go controls the visibility of identifiers outside the package:
Unlike in other languages where privacy is controlled with
publickeywords, Go marks privacy with capitalisation. [...]
I suspect that this is an issue in programming language design that people have strong opinions on, like Python's use of significant indentation; some people will find it appealing or at least be fine with it, while others will have strong negative reactions. I'm in the former camp. I don't object to this Go design decision and I find that I like some things about it.
The obvious nice thing about Go's approach is that you're never in doubt about whether an identifier is public or not when you read code. If it starts with upper case, it's public; otherwise, it's package private. This doesn't mean that a public identifier is supposed to be used generally, but at least it's clear to everyone that it could be. In other languages, you may have to consult the definition of the identifier, or perhaps a section of code that lists exported identifiers.
(The obvious drawback of Go's approach is that you can't capitalize
the start of identifier names just because you feel that they read
better that way, or because you're talking about something that is
normally written capitalized. So you have to use names like
ipv4_is_on' instead of the capitalized 'IPv4' version.)
One of Jesse Duffield's specific issues is that Go's decision here makes changing identifiers from private to public (or vice versa) a quite noisy change in your own package, since the name changes and you have to change all uses of it. One of my reactions to this is that this is a good thing. Making something public (or private) is a change in your API, and changes in your API should often be at least annoying in order to discourage you from doing them. A one-line diff for an API change feels too modest and unremarkable for such a significant thing.
(At the level of making the change, modern IDEs support 'rename identifier' operations and so the actual change is not irritating for most people. Gopls helps make this work even for people using editors like Emacs or Vi(m).)
PS: That you have to specifically annotate the JSON key names you want is, in my opinion, also not a drawback. It avoids the temptation to bias the Go names of struct fields over to what is normal and usual in JSON. JSON is a different language with different naming conventions than Go (and your Go code base).