2021-11-10 –, Room I
Semantic Versioning (MAJOR.MINOR.PATCH
) is a common approach to versioning
libraries that separates changes into fixes (PATCH
), additions (MINOR
), and
breakages (MAJOR
). Though simple, SemVer has two primary limitations that can
make it difficult for developers to work with:
-
User-facing changes, such as new features or redesigns, are not separated
from API breakages. Therefore, the compatibility between versions is harder
for maintainers to understand as the impact of MAJOR updates can vary
significantly (ex. Python1->2
vs2->3
). In consequence, some projects
now use year-based versioning or 'ZeroVer' (whereMAJOR
is always0
),
thus avoiding the question of API compatibility entirely. -
API breakages are always represented by the
MAJOR
version and do not take
into account different types of breakages, such as source vs binary
compatibility. Additionally, tooling can be used to repair many common types
of breakages (such as renaming) which do not have significant impact on how
the library is used.
The purpose of this talk is to raise awareness of these limitations, demonstrate
the use cases for having multiple levels of API versioning, and propose
alternative versioning methods that can incorporate different types of API
breakages.
- YouTube Recording
- Slide Deck (sources & additional resources in speaker notes)
Semantic Versioning (MAJOR.MINOR.PATCH
) is a common approach to versioning
libraries that separates changes into fixes (PATCH
), additions (MINOR
), and
breakages (MAJOR
). Though simple, SemVer has two primary limitations that can
make it difficult for developers to work with.
The first limitation is that user-facing changes (such as new features or
redesigns) are not separated from API breakages, which itself causes two issues:
-
Compatibility between versions is harder for maintainers to understand as
the impact of MAJOR updates can vary significantly (ex. Python1->2
vs
2->3
). Some release may cause breaking changes even though the overall
library works the same, while others may maintain backwards compatibility
but offer new (ideally better) features (ex. Java 8, which added lambdas
introducing new options for API design). -
Developers hesitate to make the
1.0.0
release (and other major releases)
for reasons related to the above as well as the effort involved in getting
key downstream dependencies to update to avoid compatibility issues. Some
projects now use year-based versioning or 'ZeroVer' (whereMAJOR
is always
0
), thus avoiding the question of API compatibility entirely .
Second, not all API breakages are the same. The most common example of this is
binary breakages, where compiled output fails to with new versions but the
source itself remains compatible. Even at the source level, some types of
breakages like renaming are 'simple' and can be automatically repaired with the
appropriate tooling, while others can require major refactors. There are also
breakages specific to the type of application - as a surprising example,
Minecraft Forge (a Minecraft API for client-side mods) encourage mods to use
separate version for mod compatibility as certain types of changes may break
player's worlds (effectively, data versioning).
There are a few potential solutions to this, and unfortunately all of them make
versions just a bit more complicated. The most straightforward one is to
maintain two versions - a true semantic version for the project, and an API
compatibility version:
- Project version:
PROJECT.FEATURE.PATCH
, which represents the high-level
changes to the project as it evolves. - API compatibility version:
PROJECT.SOURCE.BINARY
, which represents the API
breakages to the project.SOURCE
andBINARY
compatibilities work as
expected, but there is an additionalPROJECT
level for changes that have a
large impact to how the API can be used. This is currently heuristic, but the
inclusion of automated migrations may be able to formalize the idea of
'minor' breaking changes versus 'major' ones to provide strict validation.
In summary, software version needs to better account for the difference between
user-facing changes and API breakages, as well as account for different types
of breakages. One potential solution is to maintain a separate version strictly
for API compatibility, coined PROJECT.SOURCE.BINARY
, to help maintainers
understand the potential impact of updating versions.
I'm a graduate student in Computer Science at the University of Florida researching programming language design. I currently work on Rhovas, a programming language for API design and enforcement emphasizing software maintainability.
Ask Me Anything: WillBAnders@gmail.com