15 ms·
Go’s Major Versioning Sucks – From a Fanboy
- maxmcd 6y agoNot defending the import versioning strategy, but I found it helpful to read Russ Cox’s writeup on the topic: https://research.swtch.com/vgo-import https://research.swtch.com/vgo-import
- meddlepal 6y agoHelpful? Yes, sort of. Should you have to read that to understand how the module versioning system works? Absolutely not. The Go module system is a debacle. It's not intuitive and it's badly broken right now.
- klodolph 6y agoThe underlying problem is hard. I’m not aware of any packaging system that isn’t a mess. Not making apologetics here, I just don’t know what a good package system looks like.
- Tehnix 6y agoExamples of excellent package systems: - Haskell: Stack with stackage (mainly because of stackage) - Rust: Cargo and crates They are both above and beyond the rest of the space.
- klodolph 6y agoHaving used both of these... what makes them "excellent"? What makes Cargo and crates different? I'm not convinced that the stackage approach is scalable to larger ecosystems... Haskell has a fraction of Go's popularity.
- lacker 6y agoIt does kind of suck. But it’s better than npm... or rubygems... or the python ecosystem... hmm, what language’s package management system doesn’t suck?
- earthboundkid 6y agoThere’s one I like. It’s in this language from Google.
- bpodgursky 6y agoI like Maven. Fight me.
- gonzo41 6y ago#triggered
- rantwasp 6y agolevel 35 POM boss. residing in nexus!
- snuxoll 6y agoWon’t fight you - I agree. Every packaging system has stupid faults, but I generally have fewer headaches with Maven than the rest. Say what you want about Maven the build system (I’m not going to die on that hill, even though I also prefer Maven to virtually every other build tool I’ve worked with) - but Maven Central and Maven the Package Manager are solid.
- meddlepal 6y agoMe too.
- macromagnon 6y agoMy day job is on a modular monolith that uses mvn. I'm no expert but I've set up some IT's, setup build pipeline from jenkins, configure some plugins on various steps of the lifecycle, etc. and I guess it's ok, xml takes a lot of space though and it gets ridiculous on big code bases. I've been working on an android app and use gradle with the kotlin dsl. I haven't really done any programming with the build steps but this is pretty sexy and doesn't need three open/close tags every plugin. dependencies { implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar")))) implementation("com.google.android.material:material:1.2.0") Gradle used to use groovy dsl, but with kotlin the syntax is more consistent and auto complete is actually useful.
- srtjstjsj 6y agoYou don't have to read it. It Just Works. If you want bugfixes and enhancements, you upgrade the package. If you want to break your existing code, import a different package. If Haskell followed this principle, cabal hell wouldn't exist. If Windows followed this system, DLL hell wouldn't exist.
- Gibbon1 6y agoFar as I've seen Microsoft kinda learned it it's lesson. looks at watch 20 years ago. Makes you wonder about the go team, like where were they exactly between 1985 and 2005?
- srtjstjsj 6y agoThey were non-existent and when they did exist they didn't have a billion roads in funding to build everything out before launch.
- tome 6y agoCabal hell hasn't existed for quite a while. See https://cabal.readthedocs.io/en/3.4/nix-local-build-overview.html https://cabal.readthedocs.io/en/3.4/nix-local-build-overview...
- srtjstjsj 6y agoNix doesn't solve the problem of incompatible versions in one binary. Nix only solves the problem of finding compatible versions if they exist. Cabal hell is more than one problem.
- shirogane86x 6y agonix-style local builds (although the name is kinda bad) don't really have anything to do with nix, they're only the (admittedly bad) name for the new v2-* style commands (that now, at least as of cabal-3.0.0.0 and higher, are the default)
- dilap 6y agoThe major reason to have separate import paths is to support multiple major versions of the same module, which is important to not end up stuck in situations where package A wants dep D1 and package B wants dep D2, and you can't fix it. Of course, you could handle this w/ more complexity in the package manager/build system, but having separate import paths makes it very clear, and is a very "Go"ish solution. Since you're only doing this when you've got breaking changes coming in anyway, i.e., you've already got to fix the code, is it really such a big deal to update the import paths while you're at it? It's like a 2 second grep/replace... That said, having Go tell you about major version upgrades available, and also an option to fix the code to use the upgraded versions import path, are good ideas.
- xyzzy_plugh 6y ago> also an option to fix the code to use the upgraded versions import path, are good ideas. In theory this sounds good but in practice major versions can be drastically incompatible, and I wouldn't expect this to work very well at all in practice. One of the excellent features Go's use of semantic versioning enables is for lower major versions to be updated to use the higher major version implementations, as a sort of backwards-compatible shim. In this case, no tools needed.
- toast0 6y ago> Since you're only doing this when you've got breaking changes coming in anyway, i.e., you've already got to fix the code Just because the module broke compat doesn't mean you were using the things that broke though. Maybe they dropped or changed functions that you didn't use.
- harikb 6y agoI think most of the frustration I have seen comes from people expecting symmetry and uniformity in the vendor/package management solution. This is the same way some looking at an architecture diagram expects clean straight lines or aligned boundaries. That said, Go team should have made it clear right in the title (for all blog posts) that they are attempting a unique non-intuitive solution to a hard problem of transition from 'no-versioned' world of existing packages to 'semver' and the explicit desire for force people to avoid versioning if they can (just like the old days). There is nothing wrong with weird solutions. We don't have to follow Rust or Ruby or npm. Their situation is different. Of course then the protobuf or grpc team (I forget which one) went and created a new "v2" without the "v2" suffix and that pissed a few people off. Of course that was still within the reasoning Russ explained (it was a reimplementation and domain part of the URL already changed, so there was no need to add v2), and Russ probably doesn't control the particular package's team. But that is life! People still expect perfection from opensource projects.
- nemothekid 6y agoWhat I dislike about Go's solution, and somewhat about semver in general is that no one makes major version changes anymore even if they break backwards compatibility. However if I use semver internally, like it's supposed to and make regular major changes (let's say once a month), my code repo becomes a mess. After a 2 years, I'll have folders for v1 up to v24. And the fact that I have to copy -R/grep|sed, means that people will be less likely to properly signal that a backwards incompatible change has occured. Disregarding uniformity, for the reasons above I don't think their design is a good one. It treats major version changes as a rare event.
- andrewprock 6y agoFor a designed library, major version changes should be a rare event. For a collection of code that someone calls a library, all bets are off.
- Aeolun 6y agoWhat open source project is a designed library? This will just pollute the API interface for the sake of maintaining backwards compatibility.
- exacube 6y agoI think for a package versioning system to work, the ecosystem has to adhere to its principles. This is true for any package system. I think this post is an example of a package not adhereing to its principle; a major revision implies breaking changes, so it doesn't make sense to have a tool "automatically update" things for you, because theres no garuntee that it'll work. I feel like the issue is that this post doesn't totally understand how the package versioning is meant to work in the first place; if you're changing your struct names and it has no external effects, i don't see why you /want/ to semver, because you're exactly getting much value from semver'ing other than the vanity of pretty numbers. Maybe I'm missing something? Im curious if an ideal package system that would work for this particular project, but I don't think one does.
- gouggoug 6y agoExactly this. The whole point of semver is to address the issue of backward-incompatible changes breaking downstream dependencies (well, among important other things). What Go is doing is forcing you to use Semver the way it is intended to be used; from the semver specification: --- Given a version number MAJOR.MINOR.PATCH, increment the: MAJOR version when you make incompatible API changes, MINOR version when you add functionality in a backwards compatible manner, and PATCH version when you make backwards compatible bug fixes. Additional labels for pre-release and build metadata are available as extensions to the MAJOR.MINOR.PATCH format. --- If you don't want to adhere to this, just cut PATCH versions and MINOR versions. It's an absolute terrible idea though. As annoying as it may seem, there's a lot of benefits to using semver the way it is intended to: it forces you, the developer, to think a little more about how you design your code. The small amount of productivity you lose getting used to do the proper thing (i.e. cut MAJOR version, or spend more time designing your code so that it more forward thinking) will pay off in the long term. The tiny amount of productivity you gain by not doing the right thing will come back to haunt you and make you wish you had been more rigorous. His startup might be small right now, but if it becomes successful, their number of internal users might explode overnight and they'll suddenly wish they had used semver properly all along.
- kortex 6y ago
- fiatjaf 6y agoWait, don't we have versions already and they written in go.mod and matched with git tags or specific commits? Aren't they committed to go.sum? Why does that have to change if you move from v1 to v2?
- snuxoll 6y agoBecause v2 requires you change your import paths.
- m00x 6y agoCan this stop being posted? The same user posted it 12h ago.
- thegeekpirate 6y agoHey dang, you may want to look into this. OP seems to have a history of repeatedly spamming their company's posts https://news.ycombinator.com/submitted?id=lanecwagner https://news.ycombinator.com/submitted?id=lanecwagner
- m00x 6y agoDamn that sucks. I thought HN had better anti-spam methods.
- rob74 6y agoIf at first you don't succeed, try, try again...
- mplanchard 6y agoMight be worth shooting them an email, in case dang doesn’t see this message
- lanecwagner 6y agoI mean technically its a company, but really its just my personal blog. I read the HN rules and it says you can post again after ~X hours if it didn't get off of "new" (which it didn't) If there is an article I wrote that I think will do well I usually give it 2 or 3 chances
- tomc1985 6y agoSo basically this is a complaint about having to manually upgrade packages in the manifest because his small company makes lots of regular, breaking changes? If that is the case do you have to follow rules for semver? (edit- that is indeed what they do) Your users are all internal, its not like they are going to rat you out to the police for agreeing to do something different internally. It's just a couple of numbers... I don't do go but, as described, the version system seems fair and reasonable on paper, for a system designed to avoid suddenly breaking code that depends on it.
- BugWatch 6y agoWell, misreading the title as "God's" and clicking the link left me feeling somewhat disappointed.
- deklerk 6y ago> Package-Side Problems > I also find it problematic because it breaks (in my mind) one of the most useful things about module names – they reflect the file path. Sorry, this is incorrect: https://golang.org/ref/mod#module-path https://golang.org/ref/mod#module-path. A module path describes where to find it, starting with its _repository root_ (github.com, golang.org, etc) and then the subdirectory in the repository that the module is defined in (if not the root). So, if a module lives in golang.org/username/reponame/go.mod, its module path is likely golang.org/username/reponame. If a module lives in golang.org/username/reponame/dirname/go.mod, its module path is likely golang.org/username/reponame/dirname. (and so on with a /v2 folder, etc) I mention this because it appears that OP's major gripe in "package side problems" is that the /v2 dir "breaks" the (mis)conception that a module path describes _only_ the repo root. (see also: multi module repositories) > In other words, we should only increment major versions when making breaking changes No, you can increment major versions whenever you want (though it's painful to your users). But, you _should_ increment a major version when you make a breaking change. > I think a simple console warning would have been a better solution than forcing a cumbersome updating strategy on the community. Could you elaborate on how a console warning solves the problem of users becoming broken when module authors make incompatible changes within a major version? > Another problem on the client-side is that we don’t only need to update go.mod, but we actually need to grep through our codebase and change each import statement to point to the new major version: What if you need to use v2 and v4 of golang.org/foo/bar? How would you import them both without one having a /v2 suffix and a /v4 suffix? Are you proposing that users should only be able to use one major version of a library at a time? (I assume you are talking about a user upgrading to a new major, not the package author bumping to a new major. If the latter, a grep and replace is quite approachable and is shown in the blog you linked :) ) > Go makes updating major versions so cumbersome that in the majority of cases, we have opted to just increment minor versions when we should increment major versions. I'm reminded of when the "unused imports not allowed" rule was lambasted, and then goimports was released and the conversation was snuffed out. This situation feels analagous. You praise the toolchain and the good decisions in modules, but then hinge your thesis against it on "it's cumbersome". That's a valid concern, but it's likely that a tool that makes major version upgrades easier will resolve your issue. A wholly different design certainly is not needed . Check out https://godoc.org/golang.org/x/exp/cmd/gorelease https://godoc.org/golang.org/x/exp/cmd/gorelease for one tool that's under development aimed at helping version bumps. It sounds like you also need something that will create the v2 branch/directory, change all the self-referencing import paths in that branch/directory, and change the go.mod path. That sounds like an easy tool to write - I expect something like that should come out quite quickly if it doesn't already exist. ------------------- Side note: In my opinion, dependency management is a rat's nest of choices that seem good at the outset and end up with terrible consequences later on. Go modules make super well thought out decisions, and is a very very simple design, built to last for a long time without regrets. Sometimes the right answer is to work around a small problem with some tooling or documentation rather than go a totally different direction that will have large, sad consequences later. That is: all choices have downsides, but it's good to choose the best choice whose downsides can largely be tackled with easy solutions like tools and docs. Decisions like "we should build a SAT solver" have sad, sad, sad consequences that can't be tool'd and doc'd away, for example.
- donatj 6y agoI'm the author of the other recent post that made it to the front page about the trouble with Go's v2+ modules. I disagree with much of this post. > Go makes updating major versions so cumbersome that in the majority of cases, we have opted to just increment minor versions when we should increment major versions. I disagree with this, wholeheartedly. I think Go's module system making modules separate packages on major versions is actually great once you actually know how it works. The problem is simply that it's non-obvious and not well understood. I think they could have made it more obvious requiring in on v1 for starters. Beyond that you should absolutely be tagging major versions for every breaking change, and it should be something a developer has to stop and think about. Not something to be taken lightly, EVEN with regard to internal libraries. You can learn how to code things preemptively so they are flexible for additive changes and require fewer breaking changes. It's almost always worth the effort in the long run. To my posts point, the problem is just people don't know how it works, and it's design makes it something you learn late into a project, when you're tagging v2, rather than something you hit early. FWIW, my post for anyone didn't catch it: https://donatstudios.com/Go-v2-Modules https://donatstudios.com/Go-v2-Modules
- bad_user 6y agoTo add, making the version part of the namespace is the right thing to do, also because it avoids conflicts with transitive dependencies. Changing the namespace means that the same project can depend on multiple different major versions of the same library. And that's fine, because when you break compatibility, you're actually not talking about the same library. And this makes upstream developers think twice about breaking compatibility. Accretion of features should be preferable to breakage. It's what Rich Hickey talks about in his Spec-ulation talk: https://www.youtube.com/watch?v=oyLBGkS5ICk https://www.youtube.com/watch?v=oyLBGkS5ICk
- grey-area 6y agoAccretion of features should be preferable to breakage. I think most people, particularly those using Go, would agree with this. It does not follow that we should make breaking changes easier or routine, nor that we should force people to use strict semver (which is not widely used for good reasons). I see why they've done that as it simplifies assumptions but prefer the way other package managers handle this where it is left to producers and consumers to negotiate how strict they want to be.
- kissgyorgy 6y agoThe author doesn't seem to understand the idea behind this, which I very much agree with, that a new major version should be handled as a new module. You are breaking contract with your existing users, upgrading will have difficulties for them. Ross Cox explains the concept in this talk from 9:20: https://youtu.be/F8nrpe0XWRg?t=560 https://youtu.be/F8nrpe0XWRg?t=560
- JamesSwift 6y agoYou are breaking contract with _some of_ your existing users, upgrading will have difficulties for _some of_ them. I regularly update across major versions of packages in other languages and don't touch a thing other than the package version. I appreciate the version bump so I can do my due diligence to research the changes, but I just don't use the entire API of libraries in most cases so I'm not always affected by changes.
- 02020202 6y agoi guess i am still unaware of these issues since i am still using glide.
- juped 6y agoYou're always going to have trouble if you try to "enforce" semantic versioning, because it's a social commitment by project maintainers, not a theorem that can't ever possibly exist about "breaking changes" (which has a fuzzy, social definition). The best option for projects is probably to loudly declare that you are not using semantic versioning, and then use it anyway, on the good-faith basis that is the only actually possible way to use it. Declaring that you don't use it will head off trouble from people who expect it to be an impossible magic constraint, while using it will help people make sense of your version numbers.
- JamesSwift 6y ago100% agree. Semver is a _social_ construct not a _technical_ one. There is Semver the spec as originally designed, and Semver as it is currently observed. Semver has become something other than it was originally intended, likely because the idea of "breaking changes" ends up being way more encompassing than people wanted to admit [1]. To ignore that reality is a futile fight. [1] - https://www.youtube.com/watch?v=tISy7EJQPzI https://www.youtube.com/watch?v=tISy7EJQPzI
- room271 6y agoIt feels like there are some inconsistencies in this article: * we're using projects internally and don't want the hassle of rewriting import paths when making backwards-incompatible changes * we don't want to just give up on semver and publish breaking changing on the v0/1 numbering scheme I feel like you have to pick one. If it's the first, then it's actually a positive thing that explicit import path rewrites are needed in my mind at least.
- GrigoriyMikh 6y agoI have some experience developing multi-version modules(https://github.com/GrigoriyMikhalkin/sqlboiler-paginate https://github.com/GrigoriyMikhalkin/sqlboiler-paginate). And i quite disagree with this article. > new copy of the entire codebase What i have done in my module, and what i think is a common way -- is to move common codebase to common submodule, which will be used by all other versions. > Allow package maintainers to specify the major version simply by updating git tags Wouldn't it cause problems if developers would need to maintain multiple versions in parallel? > command has no way to automatically update to a new major version To be honest, i don't see why there should be auto update to new major versions, since, usually new major version means breaking changes to API.
- bigiain 6y agoGo's versioning sucks? Welcome to the Disillusioned Fanboy Club, there's a bunch of Perl guys who eventually gave up and moved to Python waiting at the bar with some war stories for you... (I'm the one alternating beers and bourbons...)
- MisterTea 6y agoI like how even the programming languages of today suffer the same technical treadmill anxiety as the frameworks and libraries they are used to author.
- srtjstjsj 6y agoI'll never understand HN's love for arbitrary ill-informed blogposts about topics that have already been debated at length by experts on the mailing lists. Writing about a complaint in a blog post and saying Tweet me is a tacit admission that the author isn't willing to face criticism from experts and just wants to get attention from the casual unininformed masses.
- larrik 6y agoThat's kind of an elitist viewpoint, though. I imagine the "experts on the mailing lists" are largely contributors, and really only represent a certain class of users of the language at large. I'm not a Go user at all, but it reminds me a bit of like back when Gnome3 took a direction completely different than what I loved about Gnome2 (Apple worship, largely), which eventually lead to projects like MATE and Cinnamon.
- geodel 6y agoNot sure. One is for end users other thing is for software engineers. Or may be we have reached to a point where people can also call data integrity or ACID compliance elitist view as it is often difficult to do.
- srtjstjsj 6y agoThe go-nuts mailing list is open. Anyone can (and many do) write in to discuss concerns. You don't have to defer completely to the authority. But if you want to be treated as one, you should be willing to engage and consult with them.
- hunterloftis 6y agoHN seems to be beginning a transition I recognize from proggit: thoughtful discussion by experts devolving into superficial critiques for the social media masses. Perhaps I’m yelling at clouds, but it’s disappointing to find an article that opens with “fanboy” and closes with “suck it” making a debut on the first page of HN.
- yobert 6y agoI interpreted those phrases as just a humorous writing style.
- ankurpatel 6y agoWhy aren't you using Ruby? https://www.youtube.com/watch?v=0D3KfnbTdWw https://www.youtube.com/watch?v=0D3KfnbTdWw
- jerrysievert 6y agothis seems to be an overall google engineering problem. go's not alone in breaking changes between minor versions, and "what worked yesterday no longer works" problems. v8 is very similar, where a very large breaking change can occur from one minor version to another. even without a loose "agreement" like semver, it feels like there is no thought to "outside the bubble".
- thayne 6y agoAnother problem is that if a branch is used for the new version (for example as hashicorps hcl/v2 does), then the master or main branch is no longer the active development branch, and developers have to know to look at a different branch. Not to mention that github search only works on the master branch.