Versioning sits at the intersection of contract and communications: what our languages and tools can enforce, and what we want to tell the people using our software.
Semantic Versioning is one answer to this: a sociotechnical contract which lets us define breaking changes. Because it is social as well as technical, though, it is ambiguous. Members of the Rust and TypeScript communities offer one kind of answer to this challenge: specification and tooling. Elm has offered another: baking it into a language-aware package manager. Unison lets evolution and versioning coexist. One group of researchers even baked type evolution into a functional programming language. Other languages and ecosystems have just thrown up their hands in the face of the inevitable edge cases and failure modes. We can do better!
How can new languages include versioning semantics in their design constraints? What tooling should we build for existing ecosystems? Above all, what should you do in your own projects?