I think Semantic versioning is pretty useful, but it can be a tad arbitrary deciding when a version number should increment. In a dynamic language, what exactly constitutes the public interface?
Rather than marking interfaces as stable, or unstable, choose explicit consistency guaruntees for interfaces.
Interfaces should be marked as
- major
- minor
- patch
Interfaces are guarunteed across version numbers of the same type. For example, an interface marked minor will not change without incrementing the minor version number.
A downstream consumer can even lock the version depending on what interfaces they are consuming.
If you're using the major and minor interfaces of version 1.2.12
choose ~1.2.12
.
If you're only consuming the major interfaces choose ~1.2
.
Any change must increment the version in some way, even by adding a fourth tag. Adding an interface can increment any version number, but which number you increment is a judgement call.
For example, if you add a whole new major API, you may just want to increment the major version number.