3 ms·
> This compounds when a library releases a new breaking major version, presumably - `%SomeStruct{}` for v2 may be different from `%SomeStruct{}` v1r2. You shou
by josevalim 2y ago
> This compounds when a library releases a new breaking major version, presumably - `%SomeStruct{}` for v2 may be different from `%SomeStruct{}` v1r2.
You should never reuse the revisions. If you launch a major version, then it means you only support r5 onwards. Do not reset it back to r1.
I am also not sure if the user needs to know the version. Remember that if I am in r5, because of subtyping, the code naturally supports r1, r2, r3, and r4, and those already have to be pretty printed. All of the work here is to "generate automatic proofs" via type signatures, no new capability or requirement is being introduced on the type representation of things.
> Documentation seems like it would be more complex, as well. If `%SomeStruct{}` in v1.0 has some shape, and you publish `v1.1`, then realize you want to update the revision of `%SomeStruct{}` with a modified (or added) field
I don't think documentation is more complex because that's something already factored in today. Functions add new options in new releases. Structs add new fields. And today we already annotate the version they were added. Perhaps this approach can automate some of it instead.
---
Other than that, we should be skeptical indeed. :)
- harrisi 2y agoAs a preamble, the discussion of versioning is difficult since it's abstract; this is difficult to discuss in general. > You should never reuse the revisions. If you launch a major version, then it means you only support r5 onwards. Do not reset it back to r1. Hm, so hypothetically a v1.0.0 of a library could begin its life with structs at a greater-than-1 revision number. This seems odd, since a major version bump is able to be breaking, but revisions don't have the same semantic meaning - it just means "it's different from before". This kind of breaks my mental model of major version bumps. I consider v1 of something to be distinct from v2, where it just happens to be that v2 typically is mostly compatible with v1. However, with revisions, it maintains that connection explicitly. I suppose the answer to that would be to rename the structs and have them start at revision 1, but now we're back at square one, where I could've just done that without revisions to maintain backwards compatibility by introducing a new struct. I'm not sure if I'm conveying what's odd about that sufficiently. Let me know if I should think on that more and try explaining it again. > I don't think documentation is more complex because that's something already factored in today. Functions add new options in new releases. Structs add new fields. And today we already annotate the version they were added. Perhaps this approach can automate some of it instead. The expectation here would be that a struct revision would be a minor version bump, correct? That seems like it kind of breaks semantic versioning, since a user of v1.0.0 wouldn't necessarily have any behavioral changes in v1.1.0, since the structs and users of the structs would be identical, right? It kind of pushes the minor version bump onto the consumer code at their discretion, but not in a typical way. Now if I bump a dependency's minor, I don't necessarily want to bump my own minor - but maybe I do? Especially if I'm a middleman library, where I pass behavior from client code to one of my dependencies. This is getting a bit confusing to consider the hypothetical situations. The main gut feeling I have about this is the same about versioning in general - it's fine until it's not, which is usually some unknown point in the future when things get complex. Kind of the gist of my hesitation is just that versioning is already a complicated problem, and introducing more variation in that seems like it really needs to be a long experiment with the expectation that it may not work. Unfortunately, the situations in which versioning rears its ugly head is typically only in long-used, complex projects. So I don't really know how a test bed for this could exist to give realistic results. As a bit of a disclaimer to everything I just said, semver is not the holy grail, in my opinion, and I think it's perfectly reasonable to experiment with it and try alternatives. Maybe part of my issue is just that I haven't entertained other versioning schemes that can sufficiently handle the difference between behavioral and representational changes. Thanks for the discussion!
- josevalim 2y ago> This kind of breaks my mental model of major version bumps. If you want, you can reset the revisions in a major version, as you could rename all modules and change the behavior of all functions, but that's hardly a good idea. Revisions are not any different. If you give a revision a new meaning in a major version, it is going to be as confusing as giving a new implementation to a function: users upgrading will have to figure out why something has subtly changed, instead of a clear error that something has gone away. > The expectation here would be that a struct revision would be a minor version bump, correct? We could make either minor and major versions (as described by semver) work. For example, if we want to allow new revisions to be minor versions, we could make it so every time you install a dependency, we automatically lock all structs it provides. This way, if a new version is out, you have to explicitly upgrade any new revision too. Of course, you could also release a new major version of the library once you introduce a new revision and _that's fine_. The goal is not to avoid major versions of the library that defines a struct, the goal is to avoid forcing its dependents to release major versions, which has a cascading effect in the ecosystem. Putting in Elixir terms, I want to be able to release Phoenix v2 without forcing LiveView to immediately become v2. LiveView should be able to stay on v1.x and say it supports both Phoenix v1 and v2 at once. But in most typed languages, if you change a field definition, it is game over. The guarantees you need to provide are not that many: as long as two revisions overlap at one point in time, you can offer a better upgrading experience to everyone, instead of assuming *all code must be updated at once*. Thanks for the convo!