3 ms·
I don't get this. The foundation upon which semantic versioning is built is that the entire semantic version is handled outside the code itself, which allows di
by solatic 9y ago
I don't get this. The foundation upon which semantic versioning is built is that the entire semantic version is handled outside the code itself, which allows different packages to communicate how they adhere to a common standard compared to each other. For instance, if I depend on A at 1.0.2, and I depend on B at 2.3.0, even if A and B have nothing to do with one another, as a consumer of A and B, I have a common contract with both of them, which makes it easier to resolve my dependency requirements. In this case, it doesn't matter if B's author never de-facto issues patch releases, because my dependency resolver tool treats them the same.
If I break that foundational assumption by putting the major version directly in the code, i.e. "semantic import versioning", what benefit do I have to track minor and patch separately? Why not just replace them both with a timestamp?
As a library packager, I fit one of the following situations:
* I don't bother with minor or patch versions at all, every release is denoted as possibly breaking. Timestamps mean nothing to me since every major release is effectively a timestamp anyway.
* I don't bother with patch versions, only minor versions. Same as before, each minor version is effectively a timestamp anyway.
* I don't bother with minor versions, only patch versions. Also effectively a timestamp.
* I bother with both minor and patch versions, but after I increase a minor version number, I never issue new patches for old minor versions, e.g. 1.0.0 -> 1.0.1 -> 1.0.2 -> 1.1.0 -> 1.1.1 -> 1.2.0. This is effectively a linear release pattern and therefore can be replaced with timestamps (if your software depends on 1.0.2, and the library packager issues 1.1.0, which is not supposed to break compatibility with 1.0.2, then why wouldn't you upgrade?).
* I bother with both minor and patch versions, and I'm committed to releasing patches for old minor versions for a certain amount of time, and therefore my linear release history conflicts with timestamps, i.e. if I release in the order of 1.0.0 -> 1.0.1 -> 1.1.0 -> 1.0.2, despite 1.0.2 having a later timestamp than 1.1.0, consumers of 1.1.0 should not "upgrade" to 1.0.2 since it may break compatibility with features introduced in 1.1.0. Implicit in this versioning practice isn't just the willingness to patch old versions of the software, but the recognition that at least a subset of your users desire to reduce risk as much as possible by avoiding even seemingly non-breaking changes and yet not avoiding patch changes. Is that even rational? Should dependency management for statically-linked binaries (not dynamically linked binaries where patch version updates can be introduced into an environment with a pre-existing binary, not networked APIs where rolling back minor versions can impact other systems using that API, not to mention the effects on backing datastores...), the statically-linked binaries in the end either will or will not compile and pass a test suite, so should dependency management for Go even bother to support this use case?
Why not have "semantic import versioning" with different major versions supporting different import paths, and then keep track of the timestamps of each dependency as it was used, with some "go dependencies update" checking for new timestamped versions (remember, none of which should result in breaking changes), keep track of which timestamped versions are used in version control and allow builds without updating the timestamps. This way a) gets you reproduceable builds, b) makes it easy to check if updates will (inadvertently, through a mistake of the library packager) break your build, and roll back if necessary, c) takes advantage of Go's property that it creates statically-linked binaries to ensure that such builds are safe in spite of the reduction in resolution vis-a-vis melding minor and patch versions into one.
Dependency resolution is then a trivial matter of compiling a list of all major versions semantically imported in the codebase, throwing them in a file matched with what were at some point the latest timestamp, and optionally checking for and updating the timestamps in the dependency resolution file. If no code references a given major version anymore, remove it and its timestamp from the dependency resolution file.