[HN Gopher] Go's major versioning sucks - From a fanboy
       ___________________________________________________________________
        
       Go's major versioning sucks - From a fanboy
        
       Author : wagslane
       Score  : 91 points
       Date   : 2022-03-04 17:39 UTC (5 hours ago)
        
 (HTM) web link (wagslane.dev)
 (TXT) w3m dump (wagslane.dev)
        
       | dilatedmind wrote:
       | Thoughts on a couple of your points:
       | 
       | - we don't need any kind of backwards compatibility, we just
       | update everything.
       | 
       | if you don't care about backwards compatibility, then you can
       | stay on v1 forever. Have you considered a monorepo? That would
       | simplify updating packages and give you the behavior you want.
       | 
       | - For the client to update, it's not a simple path change in
       | go.mod
       | 
       | if a package moves from v1 to v2, there are breaking changes in
       | either api or behavior. I think this implies more than a simple
       | change to go.mod. This also allows importing both versions of a
       | package if necessary.
        
         | dmitriid wrote:
         | > there are breaking changes in either api or behavior.
         | 
         | So instead off focusing on those changes I have to first fix
         | potential dozens of files for no reason at all.
        
       | bww wrote:
       | I've taken to just making /v0 or /v1 the root of every package I
       | maintain. Even though it's not required I think it's a good
       | practice.
       | 
       | It doesn't affect the package name (unless you choose to), only
       | the import path. So it eliminates any ambiguity about the
       | interface you intend to use and it only affects import
       | statements. Personally I think that's a good balance.
        
       | jmyeet wrote:
       | I like Go but Go has made some serious misteps (IMHO), which the
       | author touches on. Dependency management in Go is so _incredibly
       | bad_ it 's hard to fathom. Did no one on the early design team
       | ever deal with depenencies? Java is so much better here and came
       | years earlier. Go would've been so much better had they just
       | copied any of the Java options from Day One.
       | 
       | So this versioning thing is just weird and I agree with the
       | author. It's a strange thing to have opinions on and an even
       | stranger opinion to have on that thing.
       | 
       | But the thing that gets me is the whole putting
       | "github.com/username/module" into code is just awful.
        
         | philosopher1234 wrote:
         | This comment is empty of content. All you've said is "go is
         | bad" over and over again. Who would be persuaded by this?
        
         | jrockway wrote:
         | I disagree with all of this. Go's package management system is
         | a breath of fresh air compared to all other languages. Nobody
         | ever has any problem grabbing dependencies. There is only one
         | way to do it and it's built into the language runtime; if you
         | have Go installed you can install the modules. Other
         | applications on your system can, with no effort (virtualenv,
         | etc.) use different versions of those modules. Dependency
         | installation can't print messages to my terminal.
         | 
         | It's a joy.
         | 
         | Importing modules from a particular URL is a great way to
         | handle them. No central server required! Everyone can name
         | their module "utility utils" or whatever. And, nobody is
         | forcing you to use Github to host your modules, you can put
         | them on any web server you control and import them as
         | example.com/your-thing. Decentralized. Easy to use.
         | 
         | If I had two complaints, they would be:
         | 
         | 1) Library authors that think "replace" directives propagate up
         | to consumers. They do not. They are ignored when you depend on
         | the module. If you depend on one of these libraries and want
         | the workaround that the author has smoothed over with a
         | "replace" directive, you have to copy that directive to your
         | own go.mod.
         | 
         | 2) Library authors that distribute one go module for their
         | super-complicated server and their super-simple client. This
         | results in unnecessary dependency explosion. (Loki 1.x was an
         | example of this pattern. Their server depends on things like
         | Kubernetes, where their client only depends on net/http. But if
         | you naively import their client, suddenly your project depends
         | on Kubernetes.)
         | 
         | (2a would be no concept of test-only modules. Some modules are
         | only needed for the tests; it would be nice to not propagate
         | these up to consumers. It isn't an actual problem, though, just
         | a "would be kind of nice" or "I could see why they did that" if
         | it existed.)
         | 
         | Neither of these are Go issues, just maintainer issues that can
         | happen with any language.
        
         | zht wrote:
         | sorry is github.com/username/module is much worse than
         | 
         | com.arbitrary.x.y.z ?
        
           | tkiolp4 wrote:
           | In the source file one could just say
           | import "username/module"
           | 
           | And in the go.mod file the whole URL can stay
           | require github.com/username/module v1.2.3
           | 
           | I only see advantages with this approach and I imagine it's a
           | rather "easy to add" feature that can be even backwards
           | compatible.
        
             | philosopher1234 wrote:
             | What if I publish gitlab.com/username/module and i dont
             | know anything about your github project? This seems like a
             | pretty obvious disadvantage, so its surprising you failed
             | to see it. What are the advantages?
        
         | didip wrote:
         | I absolutely disagree with everything said here.
         | 
         | Maven naming convention is absolutely atrocious. And don't tell
         | me that naming convention is optional. To be part of Java
         | ecosystem Maven naming convention is a requirement.
        
       | dmitriid wrote:
       | Additionally, `go build` fails when there's a major version
       | mismatch and the error is obtuse, contains no good info on what
       | the user should do
        
       | kstenerud wrote:
       | I just keep all my go project major versions at 1, then use the
       | second number as "major" and the third number as "minor".
       | Anything incompatible increments major, anything compatible
       | increments minor.
       | 
       | This avoids all that v2, v3, v4 "nanny knows best" nonsense.
        
         | mfcl wrote:
         | Why not v0 instead? It's better suited for that purpose.
        
         | maxmcd wrote:
         | Your users are going to have a bad time if they ever import two
         | different libraries that both depend on different fake "major"
         | versions of your library. The newer of the two will be
         | selected, and any incompatibility that the different libraries
         | are not prepared to handle will break things.
        
         | paskozdilar wrote:
         | What purpose does that serve?
         | 
         | Unless your version numbers signify something to the user, you
         | might as well just use incremental integers - v1, v2, v3, etc.
        
           | pgwhalen wrote:
           | This is a comment on an article about Go package management,
           | which enforces that packages a versioned with a semver-esque
           | string.
        
             | paskozdilar wrote:
             | Your point being?
        
               | pgwhalen wrote:
               | v1, v2, v3 are not valid module versions in Go, so it
               | requires a slightly different solution.
        
               | paskozdilar wrote:
               | If you're not distributing your Go package to others as a
               | library, then you don't need to follow the Go module
               | versions. They are only enforced user-side.
        
               | pgwhalen wrote:
               | True, though if you have code in separate repositories
               | you might want to stick to _some_ versioning scheme to
               | use the module tooling to your advantage.
        
       | icholy wrote:
       | I wrote a tool that automates:                   - discovering
       | new major versions in your go.mod (gomajor list)         -
       | switching between major dependency versions. (gomajor get)
       | - updating the major version of your own module. (gomajor path)
       | 
       | https://github.com/icholy/gomajor
       | 
       | It's not perfect, but it takes a lot of toil out of working with
       | SIV.
        
         | iandinwoodie wrote:
         | SIV = Semantic Import Versioning [0]
         | 
         | 0. https://github.com/golang/go/issues/44550
        
       | paskozdilar wrote:
       | >I agree with the sentiment that we should only increment major
       | versions when making breaking changes, but more often than not
       | breaking changes are really easy to accommodate for.
       | 
       | Maybe on a small scale breaking changes don't cause issues, but
       | in large-scale development, you simply can't afford to have a
       | bunch of random CI/CD pipelines fail because a developer decided
       | to "accommodate for" a backwards-incompatible change without
       | separating it to a new import path.
       | 
       | >Using different paths for different major versions makes more
       | sense in situations where we may require two different versions
       | of the same package, you know, diamond imports and all that. This
       | is the exception, not the rule, and it seems strange to tap dance
       | around a problem that doesn't exist in most codebases.
       | 
       | Diamond imports are not as rare as you think - it's just that you
       | never run into it in your own code, but in the code of your
       | dependencies. And in Go, the problem is already solved for you so
       | you never even see the solution at work.
       | 
       | Let's say you use two libraries for, say, backend and frontend -
       | libbackend and libfrontend. Let's say they both depend on some
       | parser library, say libparser, version 1.0.
       | 
       | Now assume that libbackend upgrades the libparser dependency to
       | 2.0, but the import path stays the same and your application -
       | the one that has been successfully auto-updated on a CI/CD server
       | for years and nobody remembers how it works anymore - breaks, and
       | you have no idea why, compiler reports some weird API-
       | incompatibility errors, and now you have to spend two weeks
       | getting back into the codebase and debugging the issue, until you
       | realize you can't really fix it, because the guy developing the
       | language decided that diamond imports are "a problem that doesn't
       | exist in most codebases".
        
         | jchw wrote:
         | Honestly, my only complaint about go mod major versioning is
         | just how unintuitive versions after v1 are. While some may
         | disagree about the tradeoffs, I think the reasoning behind it
         | is totally solid, but there are definitely valid complaints
         | about the exact behavior of modules being somewhat confusing. I
         | recall confusion with trying to update a library with no tagged
         | versions, too, for example.
         | 
         | That said, I think people greatly undersell how well Go has
         | done packaging. There's, again, tradeoffs that are at least
         | arguable; but, I hate when people say things like "Didn't
         | (npm|apt|cargo|...) already solve this?" Honestly, often times
         | the answer is at best "kind of?" -- a lot of these packaging
         | and versioning problems remain without complete solutions.
         | Meanwhile, Go has some novel design choices that set it apart.
         | The module proxy is a slightly unfortunate tradeoff, but Go
         | remains one of the only languages with a good story for mostly
         | decentralized package management. The module proxy is more of a
         | hack that gets you some of the advantages of centralization
         | without strictly depending on it. But beneath that, it's nice
         | that you can pull packages from basically anywhere with several
         | VCSes.
        
         | tikkabhuna wrote:
         | It would be great to be able to declare a dependency with an
         | alias at the go.mod level. Rather than requiring the publisher
         | of a dependency to update their paths you can choose.
         | 
         | That way if a dependency does a minor upgrade that breaks your
         | code but you also need some new functionality, you could depend
         | on the old code in a particular package and new code in
         | another.
        
       | cube2222 wrote:
       | > Due to our size, we don't need any kind of backwards
       | compatibility, we just update everything.
       | 
       | Then just use 0.y.z versions and be done with it?
       | 
       | If the library constantly changes and everybody expects that,
       | then that seems fitting.
       | 
       | I like Go's major version handling very much. If it's backwards
       | incompatible, it's basically a new library under the same name
       | and development team.
       | 
       | In my opinion making major version updates so painful also
       | incentivizes not making backwards incompatible changes in
       | libraries, which results in the Go ecosystem being very stable
       | overall (something I value a lot in my day-to-day).
        
         | paskozdilar wrote:
         | >If it's backwards incompatible, it's basically a new library
         | under the same name and development team.
         | 
         | This, this, and a hundred more times this.
         | 
         | Incompatible is incompatible. There is no "kinda incompatible",
         | "99% compatible" - when it comes to dependencies, they either
         | work properly or they don't.
         | 
         | Software should be eternal. Without being strict about semantic
         | versioning, it is impossible to make it so.
        
           | pcwalton wrote:
           | Well, that's clearly not the way Go operates. Go makes
           | incompatible changes between minor releases; they just don't
           | break _type signatures_. For example, debug /pe
           | ImportedLibraries(), which is supposed to return all the
           | libraries that a module imports, was stubbed out to return
           | "nil, nil" in a minor release [1]. This is clearly an
           | incompatible change, but as it didn't cause code to fail to
           | compile the Go team deemed it semver compatible.
           | 
           | Edit: Apparently this is wrong! See replies below.
           | 
           | [1]: https://fasterthanli.me/articles/abstracting-away-
           | correctnes...
        
             | slrz wrote:
             | _For example, debug /pe ImportedLibraries(), which is
             | supposed to return all the libraries that a module imports,
             | was stubbed out to return "nil, nil" in a minor release
             | [1]_
             | 
             | I just looked at the Git history and this is plain false.
             | It already looked that way when the big source tree move
             | (src/pkg/ -> src/) was done in 2014. Tracing it back
             | further (to before Go 1.0 times, when there wasn't even a
             | builtin error interface yet and the function returned
             | os.Error), ImportedLibraries was *never* implemented in
             | debug/pe.
        
             | philosopher1234 wrote:
             | I think taking a highly abstract definition of backcompat
             | is not useful. We need a practical definition of back
             | compat. If there are no (or effectively no) downstream
             | consequences of a change, it is clearly backcompat. If
             | there are some downstream consequences, you get into
             | judgment call territory, but it still may be worth it. We
             | cannot create a perfect universal rule here, and Amos is a
             | fool for holding that standard so rigidly.
        
             | pipe_connector wrote:
             | It's worth noting that the author of that article was
             | mistaken, there was likely some other issue with their
             | software than what they described here. ImportedLibraries()
             | in the pe package has never done anything other than
             | returned nil, nil. This wasn't changed in a minor release.
             | You can browse the source history here: https://github.com/
             | golang/go/blame/master/src/debug/pe/file....
        
           | slaymaker1907 wrote:
           | No, it really shouldn't be. It makes it impossible to
           | distinguish between those "theoretically this a breaking
           | change" kind of changes and the "you're going to need to
           | rewrite a bunch of code" kind of changes. At the very least
           | libraries should be allowed to define how the library must be
           | used in order for their semantic versioning to apply. For
           | languages which allow "import * from x" style imports, the
           | library should still be allowed to add in new functions
           | without that being a breaking change.
           | 
           | If you want your software to be pristine forever, you really
           | need to pin your dependencies, ideally via copying the source
           | of the library into your repo so you aren't reliant on a
           | package manager being available in the future. For library
           | developers, regardless of versioning scheme you need to avoid
           | ANY breaking changes whatsoever. Instead of changing an
           | existing function, introduce a new one so that your
           | downstream users can still access the old behavior while
           | keeping up to date. Trust me when I say this will be much
           | easier most of the time than patching old versions with
           | security updates and bug fixes.
        
             | paskozdilar wrote:
             | API is API. Either a library implements it or not. There is
             | no room for "just this small API change uwu" in large-scale
             | development.
        
           | slimsag wrote:
           | How do you define "compatible"? Semver doesn't define it.
           | 
           | An HTTP server can remain API compatible, and still drop or
           | break support for a major feature you care about. Surely you
           | don't want that to ship in a patch release.
           | 
           | Adding a new struct field, even a private one, can break API
           | compatibility in Go if people use your struct without named
           | fields. Do you want a new library in this case or not?
           | 
           | Semver doesn't cover CLI compatibility, either, do you want a
           | CLI redesign to remain v1 or become v2?
           | 
           | Nuance matters. Stating to "just be strict about semantic
           | versioning" doesn't help, semver is fuzzy.
        
             | pgwhalen wrote:
             | This is a good point, arguably even fixing any bug breaks
             | compatibility, at least if you're a kernel maintainer. An
             | (in)famous Linus quote:                   If a change
             | results in user programs breaking, it's a bug in the
             | kernel. We never EVER blame the user programs.
        
               | slaymaker1907 wrote:
               | I don't think this is all that useful of a quote outside
               | of operating systems (and even there I still find it of
               | questionable value). You really need to define how people
               | can use your software (at least at a high level) and
               | receive backwards compatibility guarantees.
               | 
               | Even in the Linux example, kernel modules do not receive
               | compatibility guarantees because it is difficult to keep.
               | You may need to rewrite your module depending on how it
               | is written when upgrading the kernel. It also doesn't
               | apply in case of security vulnerabilities and certain
               | classes of bugs. Technically viruses can be user programs
               | which rely on those vulnerabilities and even excluding
               | those, there are cases where some API seems benign but
               | later turns out to be flawed (like precise timers in the
               | context of browsers).
        
               | noctune wrote:
               | I think it's a difference in what one considers to be
               | "the API". Linux is very much to the de facto API side,
               | whereas some other project might be more one the de jure
               | API side with a rigid specification and allowing any
               | change within that. Most things are probably somewhere
               | between the two.
        
             | paskozdilar wrote:
             | > How do you define "compatible"?
             | 
             | I define it as "provides the equivalent API".
        
           | viraptor wrote:
           | There's an extra dimension here though - support. Projects
           | don't have unlimited resources which in majority of the cases
           | means that only one major version is live.
           | 
           | For downstream consumers that gives 2 options: get stuck on
           | an old version silently sometimes, or deal with an occasional
           | breakage during usual dependencies updates. If the old
           | version is used for talking to some external service, you
           | will break one day.
        
           | Groxx wrote:
           | There absolutely is fuzziness in incompatibility.
           | 
           | I can change one part of an API and leave another untouched -
           | that's part compatible, part incompatible. It's only an issue
           | if you used the changed part.
           | 
           | (If you think that first one always counts... what if the
           | changed part is literally called
           | YouMustNotUseThisFunctionOrYourCodeWillAlwaysBreak() ? It's
           | clearly implied to not be part of your _intended_ API,
           | despite technically being part of it.)
           | 
           | I can add something to a type, in a way that's backwards
           | compatible at compile time... but common reflection patterns
           | might cause everyone's code to explode.
           | 
           | I can make a change that solves a bug that someone was
           | accidentally relying on by doing the wrong thing, but doesn't
           | affect compile-time behavior, nor runtime for anyone using
           | the library the way they should. But that bug-user's code is
           | now broken, is this an incompatible change?
        
             | paskozdilar wrote:
             | > I can change one part of an API and leave another
             | untouched - that's part compatible, part incompatible. It's
             | only an issue if you used the changed part.
             | 
             | When you commit to a version 1, you assume that every user
             | of the package is using every feature you provide through
             | your official API. If you break any slightest piece of the
             | API, you've broken compatibility. It might "kinda work" for
             | many users, but it will almost surely cause significant
             | pain for many others.
             | 
             | > I can make a change that solves a bug that someone was
             | accidentally relying on by doing the wrong thing, but
             | doesn't affect compile-time behavior, nor runtime for
             | anyone using the library the way they should. But that bug-
             | user's code is now broken, is this an incompatible change?
             | 
             | It is not an incompatible change, and it is the
             | responsibility of the bug-user's code to fix the bug in his
             | program.
             | 
             | Of course, when such a thing happens on a large-enough
             | scale, the API developer sometimes cannot afford to "fix"
             | the behavior and force countless users to fix their
             | programs, so the quirk just becomes de-facto part of the
             | API.
        
             | ithkuil wrote:
             | > I can make a change that solves a bug that someone was
             | accidentally relying on by doing the wrong thing ...
             | 
             | There is even a "law" for that: https://www.hyrumslaw.com/
        
         | mftb wrote:
         | This is the answer. It's all internal, he says in his case.
         | They know what they're doing with their own stuff. Staying on
         | v0 is just another signifier that it's one of their internal
         | things, they need to handle specially.
        
         | bborud wrote:
         | > Then just use 0.y.z versions and be done with it?
         | 
         | Yeah, that would work, except a lot of people read 0.x.y
         | versions as "alpha quality". Regardless of the actual code
         | quality.
        
           | pgwhalen wrote:
           | Is this a problem inside a small company though? I would
           | expect there to be much better signals about how alpha-ish a
           | library is in that setting (i.e. talking to your coworkers).
        
             | bborud wrote:
             | Funny you should ask. Yes, it can be when you use third
             | party software and people have version number hangups.
        
               | pgwhalen wrote:
               | Version number hangups are indeed a problem, I don't mean
               | to suggest my organization has escaped them. But if you
               | can wade through those successfully, the technical
               | solution itself often does make sense.
        
           | gowld wrote:
           | If you expect a certain version to be valuable long term,
           | promote it to Z.0 version. It's OK to to have
           | AwesomeButRapidlyChangingLibrary-2022.03.0 branched from
           | 0.y.z.
        
             | bborud wrote:
             | Or, as some people recommend, skip directly to V2 as the
             | first version.
        
           | philosopher1234 wrote:
           | This is true and unfortunate, but I think the engineering
           | value of meaningful versions is important and I expect that
           | with enough time people will adapt to understanding a
           | different meaning of 0.x.
           | 
           | And besides, people are right to understand 0.x is risky b.c
           | you are not guaranteeing backwards compatibility.
        
           | digisign wrote:
           | If it is changing all the time, then it is "alpha quality."
        
         | pgwhalen wrote:
         | >> Due to our size, we don't need any kind of backwards
         | compatibility, we just update everything.
         | 
         | > Then just use 0.y.z versions and be done with it?
         | 
         | FWIW, I also work in an organization that thinks of libraries
         | this way, and we've found success and simplicity in versioning
         | (for production) our Go libraries as 0.X.0 where X is just a
         | monotonically increasing number generated by the build
         | pipeline.
        
           | mananaysiempre wrote:
           | ... Zerover[1]?
           | 
           | [1] https://news.ycombinator.com/item?id=28154187
        
             | pgwhalen wrote:
             | Yep! I think it's fair to needle open source software, but
             | it absolutely makes sense for a lot of internal development
             | to adopt this sort of versioning policy.
        
       | earthboundkid wrote:
       | > This means major version changes are a fairly regular
       | occurrence. Some say that we should just stay on v0, and that's a
       | reasonable solution. The problem is these ARE production packages
       | that are being used by a wide number of services. We want to
       | Semver.
       | 
       | This is a lame problem. v0.Major.Minor-Patch. Done. Yes, semver
       | includes an optional dash for a fourth parameter, and Go supports
       | it.
        
         | pa7ch wrote:
         | How does the forth parameter affect version ordering though?
         | Since its a pre-release I would assume v0.1.2-3 actually comes
         | before v0.1.2. You'd have to ensure make all your versions have
         | the dash I guess.
        
           | munificent wrote:
           | _> Since its a pre-release I would assume v0.1.2-3 actually
           | comes before v0.1.2._
           | 
           | That's correct.
           | 
           | (The really fun weird corner of semver is build suffixes.
           | According to semver 2.0.0, v1.2.3+4 and v1.2.3+5 have no
           | specified relative ordering. According to semver 2.0.0-rc.1,
           | build suffixes are ordered.)
        
       | henvic wrote:
       | I recognize that this might be a PITA, but for me there is no
       | easy way out of problems related to major versioning, and the
       | clear answer is sticking with version 0.x.x for a considerate
       | amount of time.
       | 
       | It seems people rush to publish version 1.0 (or 2.0, etc.) of
       | their libraries, when they'd be better off just sticking with
       | version 0.x.
       | 
       | It's not like that a package isn't ready for production because
       | it is < 1.0. It might as well be. If you're an early adopter, I'd
       | say that is even welcome: you're aware that its API might chance,
       | that its quite new, etc. It gives more confidence than a package
       | with version 7.x (at least in informing you that it's prone to
       | changes, and allowing you to make an informed choice), IMHO.
        
         | no_wizard wrote:
         | Lots of teams, companies, and general best practice all have
         | policies and/or guidelines around versioning, and its usually
         | something to the effect of _wait till the package is 1.0 to use
         | it in production_. I think that 's why there is always a "race"
         | to the 1.0
         | 
         | I think we should just push for more date based versioning, for
         | instance, CalVer[0]
         | 
         | [0]: https://calver.org/
        
           | earthboundkid wrote:
           | All of my Go packages are CalVer v0.year.increment because
           | I'm just a lone developer, so nothing I release can be v1. To
           | be an actual v1, there needs to be a team of people who are
           | committed to keeping a package going indefinitely. I
           | understand why people want to use v1, because it sounds cool
           | and stable, but realistically if you are just releasing code
           | on your own, it is not and cannot be v1.
        
             | zufallsheld wrote:
             | Why would a package released as v1 need to be supported
             | indefinitely?
        
           | [deleted]
        
           | paskozdilar wrote:
           | Semantic versioning serves a purpose - it makes it possible
           | to automatically update dependencies to the latest compatible
           | version without breaking code.
           | 
           | If you don't need automatic updates, then any kind of
           | versioning is fine. Hell, you could get away with just using
           | a single incrementing integer. v1, v2, v3, ..., v225883, etc.
        
       | fsociety wrote:
       | My biggest pain with Go modules has been the fact that projects
       | use 0ver [0] and some are backwards compatible updates and others
       | aren't. But it was designed in a way to force everyone to use
       | semver. It gets worse if you have a mono-repo because OSS
       | projects may depend on a module which depends on a module which
       | uses 0ver but minor versions are backwards incompatible. This
       | means that dependency conflicts become a nasty situation. Then
       | you get into opinion-based world where: - everyone should just
       | use semver properly. - everyone shouldn't use v0 for production.
       | Or variations on this where some are production-ready and others
       | aren't. - everyone should use v0 and only have backwards
       | compatible updates. Then fork if you have an backwards
       | incompatible update. Feels like everyone loses with how this
       | works today. [0] https://0ver.org
        
       | john567 wrote:
       | Don't make breaking changes. Make new APIs then transition users.
       | Remove old APIs as needed when usage is very low.
       | 
       | Niche people that depend on the old API will stick to an older
       | version and be happy with that.
        
       | olliej wrote:
       | Sorry, my reading of the article seemed to imply that I should
       | have multiple copies of a project in a single repository, one for
       | each major version?
       | 
       | That's what branches and tags are for, so what am I missing?
        
         | paskozdilar wrote:
         | Let's say you write a Go program using a dependency
         | "github.com/foo/bar" v1.0 and post it on the Internet as a
         | regular file (or multiple files), without using git. Someone
         | else downloads the program and runs the usual Go commands:
         | go mod init example.com/program         go mod tidy         go
         | build
         | 
         | The go tool will see the "github.com/foo/bar" path in the
         | import statements, download the code from the repository,
         | compile and everything will work.
         | 
         | Now, let's say the "github.com/foo/bar" module gets a
         | backwards-incompatible change, but does not change the import
         | path, and you attempt to do the above process again. This time,
         | the go tool will download the incompatible version of
         | "github.com/foo/bar" and the build will fail - or even worse,
         | succeed but have some logic bugs that will go unnoticed until
         | some massive shit happens.
        
           | masklinn wrote:
           | So the Go project used a lazy solution to a hard problem
           | (packages are git repositories without even a ref as version)
           | instead of, say, having an actual versioning scheme,
           | therefore you get to implement this by hand using nonsense
           | logic.
        
             | paskozdilar wrote:
             | Actually, Go used a very elegant solution to a hard problem
             | of modularization: directories. A directory == a package.
             | Import path specifies a path to the directory that contains
             | a package.
             | 
             | A package may, or may not, decide to have a stable API and
             | document it. If it does, and it commits to the version 1.0,
             | then it basically gives a promise: "This package will not
             | change its API in a way that will break correctness of
             | currently-correct programs that use it".
             | 
             | Since a package == a directory, if you want to keep package
             | compatibility, you must not change the contents of the
             | import directory. Therefore, you need to create a new one,
             | preferably called v2/, to put the new code in.
        
               | tkiolp4 wrote:
               | It's an elegant solution for packages that don't use
               | versioning features like git tags. For every other
               | package out there that uses git tags, it's just a lazy
               | solution: why on earth would I want to specify the
               | version in the path? Git tags solves that problem in a
               | more elegant way (both from the side of the maintainers
               | and users).
        
               | paskozdilar wrote:
               | > it's just a lazy solution: why on earth would I want to
               | specify the version in the path?
               | 
               | It's not a lazy solution, just opinionated.
               | 
               | Personally, I love the fact that just by seeing the
               | import path I know exactly which codebase is used. I
               | don't have to open my go.mod, see which version I'm
               | using, clone the repository, dig through the history to
               | check out a specific tag and see what code is actually
               | used in my program... I just open the repo in my browser
               | and browse through the directories. There isn't a single
               | system on earth that doesn't support directories!
               | 
               | I also love the fact that I don't have to know s**t about
               | git to use Go. If Go was to suddenly switch to git tags,
               | not only would it break a massive amounts of existing
               | code, but would basically force everyone to learn about
               | git tags just to be able to see what code are they using,
               | which would raise the amount of paperwork I have to fill
               | in order to work on the thing I care about. Go is
               | fundamentally against needless paperwork.
        
             | chabad360 wrote:
             | I'm very confused. Go literally uses refs as versions.
             | However to allow for situations where you'll need to update
             | a module because of a bug fix or something like that, Go
             | allows you to safely automatically update to the latest
             | minor version, on the assumption that there are no API
             | breaking changes. If there is such a change, Go wants the
             | module owner to increment the major version so you don't
             | get bugs just from updating.
             | 
             | I will agree that the ergonomics of this method aren't
             | perfect by any means. But what it does accomplish, is that
             | it forces you to declare your dependency explicitly (which
             | seems to be one of Go's underlying principles).
        
             | gowld wrote:
             | There is a versioning scheme. It's not golang's fault if a
             | publisher doesn't use it.
             | 
             | https://go.dev/doc/modules/version-numbers
        
       | [deleted]
        
       | geodel wrote:
       | Kinda useless article. Maybe need to think why they need so many
       | major versions so often to break backward compatibility. And if
       | one just updates minor versions none of those problems occur.
        
       | donatj wrote:
       | I wrote about this a while ago, it was previously discussed here
       | 
       | https://news.ycombinator.com/item?id=24429045
       | 
       | The post itself
       | 
       | https://donatstudios.com/Go-v2-Modules
        
       | iio7 wrote:
       | > In fact, we use Go on the front and backend at Qvault, and
       | we've found that it's wonderful to have standardized formatting,
       | vetting, and testing across the entire Go ecosystem
       | 
       | Well, that doesn't seem to be true as Qvault is clearly a
       | Wordpress powered website.
        
         | lowmagnet wrote:
         | I worked at a telco with Wordpress powered website. Our control
         | panels and backend systems were not Wordpress.
        
         | cryvate1284 wrote:
         | Are you sure that's not just the sales website?
         | 
         | Quite common for the sales website to be wordpress and separate
         | from the frontend of the product.
        
       ___________________________________________________________________
       (page generated 2022-03-04 23:00 UTC)